GLSL Three.js vertex and fragment shader is not working [closed] - three.js

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last year.
Improve this question
I am new to Three.js and GLSL and I am trying to load my vertex and fragment shader in HTML format. However, it was not working, and I am facing the error
Uncaught TypeError: Cannot read properties of null (reading 'textContent')
Is there a reason why this happen and how can I solve this?
I tried to do my vertex and fragment shader inline with the HTML document.
File index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>OBJ loader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #FFF;
color: #FFF;
margin: 0px;
overflow: hidden;
}
#info {
color: #FFF;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display: block;
}
#info a, .button {
color: #F00;
font-weight: bold;
text-decoration:
underline;
cursor: pointer
}
</style>
</head>
<body>
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/loaders/OBJLoader.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<script id="vertex_shader" type="x-shader/x-vertex">
layout (location = 0) in vec2 pos;
void main() {
gl_Position = vec4(pos, 0.0f, 1);
}
</script>
<script id="fragment_shader" type="x-shader/x-fragment">
uniform vec2 uResolution;
uniform float uTime;
out vec4 outColor;
precision highp float;
uniform sampler2D tex;
void main()
{
vec2 uv = gl_FragCoord.xy/uResolution;
float time = uTime * 0.4;
// Apply pixelate effect
//vec2 uv_pixel = uv;
vec2 uv_pixel = floor(uv * (uResolution/4)) / (uResolution/4);
vec4 col1 = vec4(0.510, 0.776, 0.486, 1.0);
vec4 col2 = vec4(0.200, 0.604, 0.318, 1.0);
vec4 col3 = vec4(0.145, 0.490, 0.278, 1.0);
vec4 col4 = vec4(0.059, 0.255, 0.251, 1.0);
// Displacement on top of y
vec3 displace = texture(tex, vec2(uv_pixel.x, (uv_pixel.y + time) * 0.05)).xyz;
displace *= 0.5;
displace.x -= 1.0;
displace.y -= 1.0;
displace.y *= 0.5;
// Color
vec2 uv_tmp = uv_pixel;
uv_tmp.y *= 0.2;
uv_tmp.y += time;
vec4 color = texture(tex, uv_tmp + displace.xy);
// Match to colors
vec4 noise = floor(color * 10.0) / 5.0;
vec4 dark = mix(col1, col2, uv.y);
vec4 bright = mix(col3, col4, uv.y);
color = mix(dark, bright, noise);
// Add gradients (top dark and transparent, bottom bright)
float inv_uv = 1.0 - uv_pixel.y;
color.xyz -= 0.45 * pow(uv_pixel.y, 8.0);
color.a -= 0.2 * pow(uv_pixel.y, 8.0);
color += pow(inv_uv, 8.0);
// Make the waterfall transparent
color.a -= 0.2;
outColor = vec4(color);
}
</script>
<script>
var clock = new THREE.Clock();
var delta = clock.getDelta(); // Seconds.
var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 degrees) per second
var container, stats;
var camera, scene, renderer, texture;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
const customMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 1.0 },
map: { value: texture },
resolution: { value: new THREE.Vector2() }
},
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
});
init();
animate();
//var texture = new THREE.Texture();
new THREE.OBJLoader().load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/ladybug.gltf', function (object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material = customMaterial;
}
});
scene.add(object);
});
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(15, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 100;
// Scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight(0x111130);
scene.add(ambient);
var directionalLight = new THREE.DirectionalLight(0xFFEEFF);
directionalLight.position.set(1, 1, 0.5);
scene.add(directionalLight);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
document.addEventListener('mousemove', onDocumentMouseMove, false);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function onDocumentMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 2;
mouseY = (event.clientY - windowHalfY) / 2;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x += (mouseX - camera.position.x) * .05;
camera.position.y += (- mouseY - camera.position.y) * .05;
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
</script>
</body>
</html>

There are typos in your code. The IDs of your shader script tags are vertex_shader and fragment_shader but you are using vertexshader and fragmentshader in your JavaScript code. You are missing the underscores.

Related

Not getting expect result from THREE.js/GLSL code

Apologies for the vague question but I wasn't sure how to phrase this. I'm trying to write some THREE.js/GLSL code that produces a circular gradient (for some SDF stuff). With the code below I would expect to see the gradient on the plane, but the plane remains white and nothing else renders on it. Can anyone tell me where I'm going wrong?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script type="x-shader/x-vertex" id="sdfVS">
varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="sdfFS">
precision mediump float;
uniform vec2 u_resolution;
varying vec2 vUv;
float circle(vec2 pos, float radius)
{
return distance(pos, vec2(radius));
}
void main()
{
vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;
float circle1 = circle(pos, 0.5);
vec3 color = vec3(circle1);
gl_FragColor = vec4(color, 1.0);
}
</script>
<script src="../../js/three.min.js"></script>
<script>
var scene, camera, renderer, aspect, geometry, material, plane;
var container;
var frustumSize = 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera( 0.5 * frustumSize * aspect / - 2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1 );
cameraOrthoHelper = new THREE.CameraHelper( camera );
scene.add( cameraOrthoHelper );
var width = 1;
var height = 1;
geometry = new THREE.PlaneGeometry(width, height);
material = new THREE.ShaderMaterial( {
vertexShader: document.getElementById('sdfVS').textContent,
fragmentShader: document.getElementById('sdfFS').textContent,
side: THREE.DoubleSide
} );
plane = new THREE.Mesh( geometry, material );
plane.rotation.x = 0;
plane.rotation.y = THREE.Math.degToRad( -90 );
plane.rotation.z = 0;
scene.add( plane )
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x = -1;
camera.position.y = 0;
camera.position.z = 0;
camera.lookAt( scene.position );
camera.updateMatrixWorld();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Can't get this line:
vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;
Seems like you want to calculate uv coordinates, but if so, then you already have uvs (passed in vUv).
You can do the thing this way, as an option:
var scene, camera, renderer, aspect, geometry, material, plane;
var container;
var clock = new THREE.Clock();
var frustumSize = 2;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(0.5 * frustumSize * aspect / -2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 1);
cameraOrthoHelper = new THREE.CameraHelper(camera);
scene.add(cameraOrthoHelper);
var width = 1;
var height = 1;
geometry = new THREE.PlaneGeometry(width, height);
material = new THREE.ShaderMaterial({
uniforms: {time: {value: 0}},
vertexShader: document.getElementById('sdfVS').textContent,
fragmentShader: document.getElementById('sdfFS').textContent,
side: THREE.DoubleSide
});
plane = new THREE.Mesh(geometry, material);
plane.rotation.x = 0;
plane.rotation.y = THREE.Math.degToRad(-90);
plane.rotation.z = 0;
scene.add(plane)
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x = -1;
camera.position.y = 0;
camera.position.z = 0;
camera.lookAt(scene.position);
camera.updateMatrixWorld();
var t = clock.getElapsedTime();
material.uniforms.time.value = t;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script type="x-shader/x-vertex" id="sdfVS">
varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="sdfFS">
precision mediump float;
uniform float time;
uniform vec2 u_resolution;
varying vec2 vUv;
float circle(vec2 uv, vec2 pos, float radius) {
return smoothstep(radius, 0., length(uv - pos));
}
void main()
{
vec2 uv = vUv * 2. - 1.;
vec2 pos = vec2(cos(time), sin(time));
float circle1 = circle(uv, pos, 1.0);
vec3 color = vec3(circle1);
gl_FragColor = vec4(color, 1.0);
}
</script>

Plane Flickering on using ShaderMaterial in Threejs

I'm trying to apply a texture on a very large plane (1000x1000 which is also scaled 10 times) by using RepeatWrapping. It looks good when I use MeshBasicMaterial but it flickers when I use ShaderMaterial. Below is My Code.
<!DOCTYPE html>
<html>
<head>
<title>MeshShaderMaterialExample</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<script src="https://unpkg.com/three#0.85.0/examples/js/controls/TrackballControls.js"></script>
<script src="js/TWEEN.js"></script>
<style type="text/css">
body {
width: 100%;
height: 100%;
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script>
var camera, scene, renderer;
var container, mesh, geometry;
var controls, effect;
var tweenUpdate="false";
var tweenOver="true";
var textureData=
{
"texture_0":
{
"img":"gman.png"
},
"texture_1":
{
"img":"gman.png"
}}
var magicPosition = { magicTrans:0 };
var magicTarget = { magicTrans:1 };
var magicTween = new TWEEN.Tween(magicPosition).to(magicTarget, 1000);
magicTween.easing(TWEEN.Easing.Linear.None);
var currentTexture=0;
var nextTexture=0;
var uniforms = {
textures: {
value: []
},
repeat: {
type: 'f',
value: 100
},
transition: {
value: 0
},
currentUniform: {
value: 0
},
nextUniform: {
value: 0
}
};
var textureLoader = new THREE.TextureLoader();
var pics=[];
for (var i = 0; i < Object.keys(textureData).length; i++) {
var ass="texture_"+i;
pics[i]= textureData[ass].img;
console.log(pics[i]);
}
pics.forEach((p, idx)=>{
textureLoader.load(p, function(tex){
tex.needsUpdate = true;
uniforms.textures.value[idx] = tex;
uniforms.textures.value[idx].needsUpdate = true;
// console.log(tex);
uniforms.textures.value[idx].minFilter = THREE.LinearFilter;
})
});
var vertShader = `
varying vec2 vUv;
uniform float repeat;
void main()
{
vUv = repeat * uv;
vec4 mvPosition = modelViewMatrix * vec4(position, 1 );
gl_Position = projectionMatrix * mvPosition;
}
`;
var fragShader = `
uniform sampler2D textures[` + pics.length + `];
uniform float transition;
uniform float currentUniform;
uniform float nextUniform;
varying vec2 vUv;
vec4 getTexture(int index){
for(int i = 0; i < ` + pics.length + `; i++){
if (i == index){ return texture2D(textures[i],vUv); }
}
}
void main()
{
float chunk = 1. / ` + 1 + `.; // amount of transitions = 1
float t = floor(transition / chunk);
int idx0 = int(currentUniform);
int idx1 = int(nextUniform);
gl_FragColor = mix(
getTexture(idx0),
getTexture(idx1),
(transition - (float(t) * chunk)) * ` + 1 + `.
);
}
`;
window.onload=function()
{
init();
animate();
}
function init(){
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
controls = new THREE.TrackballControls( camera,renderer.domElement );
camera.position.z = 500;
console.log(camera.fov);
scene = new THREE.Scene();
scene.add(camera);
var magicPlaneMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertShader,
fragmentShader: fragShader,
side: THREE.DoubleSide
});
for (var i = 0; i < Object.keys(textureData).length; i++) {
uniforms.textures.value[i].wrapS = uniforms.textures.value[i].wrapT = THREE.RepeatWrapping;
uniforms.textures.value[i].needsUpdate = true;
}
// for (var i = 0; i < Object.keys(textureData).length; i++) {
// uniforms.textures.value[i].wrapS = uniforms.textures.value[i].wrapT = THREE.RepeatWrapping;
// uniforms.textures.value[i].needsUpdate = true;
// }
var magicPlaneGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 16, 16);
var magicPlaneMesh = new THREE.Mesh(magicPlaneGeometry, magicPlaneMaterial);
magicPlaneMesh.position.y = -500;
magicPlaneMesh.rotation.x = Math.PI / 2;
magicPlaneMesh.scale.x=10;
magicPlaneMesh.scale.y=10;
scene.add(magicPlaneMesh);
changeMagicPlane(currentTexture);
document.addEventListener( 'wheel', onDocumentMouseWheel, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseWheel( event ) {
var fov = camera.fov + event.deltaY * 0.05;
camera.fov = THREE.Math.clamp( fov, 10, 75 );
console.log(camera.fov);
camera.updateProjectionMatrix();
}
function animate() {
if(tweenUpdate=="true")
{
TWEEN.update();
}
renderer.render( scene, camera );
controls.update();
requestAnimationFrame( animate );
}
function changeMagicPlane(asset){
var assNum= parseInt(asset);
nextTexture = assNum;
uniforms.nextUniform.value = nextTexture;
console.log("Cuurent: "+currentTexture);
console.log("Next: "+nextTexture);
magicTween.start();
tweenUpdate="true";
tweenOver="false";
}
magicTween.onUpdate(function(){
uniforms.transition.value = magicPosition.magicTrans;
});
magicTween.onComplete(function(){
tweenUpdate="false";
tweenOver="true";
clicked="false";
//console.log("Am i complete?");
magicPosition.magicTrans=0;
currentTexture=nextTexture;
uniforms.currentUniform.value = currentTexture;
console.log("Current: "+currentTexture);
});
</script>
</body>
</html>
I'm trying to use ShaderMaterial for crossfading effect. My texture image is 256*256 pixels.
Working snippet. Tween.js is used from here (http://learningthreejs.com/blog/2011/08/17/tweenjs-for-smooth-animation/). gman.png is from here (https://i.imgur.com/ZKMnXce.png)
You've disabled the trilinear texture filtering (mipmaps), by setting the texture minifying function (.minFilter) to the value THREE.LinearFilter:
uniforms.textures.value[idx].minFilter = THREE.LinearFilter;
This causes Moire effects.
Activate the trilinear texture filtering by THREE.LinearMipMapLinearFilter (this is default):
uniforms.textures.value[idx].minFilter = THREE.LinearMipMapLinearFilter;
Anyway your (fragment) shader code has undefined behavior and the mip-mapping won't work:
vec4 getTexture(int index){
for(int i = 0; i < ` + pics.length + `; i++){
if (i == index){ return texture2D(textures[i],vUv); }
}
}
void main()
{
// ....
gl_FragColor = mix(
getTexture(idx0),
getTexture(idx1),
(transition - (float(t) * chunk)) * ` + 1 + `.
);
See OpenGL ES Shading Language 1.00 Specification - 13 Acknowledgements; page 107:
5 Indexing of Arrays, Vectors and Matrices
[...]
Samplers
GLSL ES 1.00 supports both arrays of samplers and arrays of structures which contain samplers. In both these cases, for ES 2.0, support for indexing with a constant-index-expression is mandated but support for indexing with other values is not mandated.
[...]
6 Texture Accesses
Accessing mip-mapped textures within the body of a non-uniform conditional block gives an undefined value. A non-uniform conditional block is a block whose execution cannot be determined at compile time.
Do the texture lookup in the block scope of main and use a constant-index-expression, for the index of the texture sampler array:
e.g.
float a = transition - float(t) * chunk;
gl_FragColor = mix(texture2D(textures[0], vUv), texture2D(textures[1], vUv), a);

Removing moire patterns produced by GLSL shaders

I have setup this minimal test case, which you can easily see the moire patterns produced by undersampling the oscilating red colour using a custom fragment shader (jsfiddle).
What is the general technique for removing such patterns using GLSL? I assume it involves the derivatives extension, but I've never quite understood how to implement it. I basically have to do anti-aliasing, I think?
var canvas = document.getElementById('canvas');
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
var camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientWidth, 1, 1000);
var geometry = new THREE.SphereGeometry(50, 50, 50);
var material = new THREE.ShaderMaterial({
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
camera.position.z = 100;
var period = 30;
var clock = new THREE.Clock();
render();
function render() {
requestAnimationFrame(render);
if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
sphere.rotation.y -= clock.getDelta() * 2 * Math.PI / period;
renderer.render(scene, camera);
}
html, body, #canvas {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script>
<canvas id="canvas"></canvas>
<script id="vertex-shader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
#define M_TAU 6.2831853071795864769252867665590
varying vec2 vUv;
void main() {
float w = sin(500.0 * M_TAU * vUv.x) / 2.0 + 0.5;
vec3 color = vec3(w, 0.0, 0.0);
gl_FragColor = vec4(color, 1.0);
}
</script>
Update: I've tried to implement super-sampling, not sure if I have implemented it correctly but it doesn't seem to help too much.
Unfortunately, the moire pattern here is a result of the high-contrast lines approaching the Nyquist Frequency. In other words, there's no good way to have a 1- or 2-pixel-wide high-contrast line smoothly shift to the next pixel over, without either introducing such artifacts, or blurring the lines to be indistinguishable.
You mentioned the derivatives extension, and indeed that extension can be used to figure out how quickly your UVs are changing in screen space, and thus, figure out how much blurring is needed to sort of sweep this problem under the rug. In the modified version of your own example below, I attempt to use fwidth to turn the sphere red where the noise gets bad. Try playing with some of the floats that are defined to constants here, see what you can find.
var canvas = document.getElementById('canvas');
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
var camera = new THREE.PerspectiveCamera(75, canvas.clientWidth / canvas.clientWidth, 1, 1000);
var geometry = new THREE.SphereGeometry(50, 50, 50);
var material = new THREE.ShaderMaterial({
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
camera.position.z = 100;
var period = 30;
var clock = new THREE.Clock();
render();
function render() {
requestAnimationFrame(render);
if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
sphere.rotation.y -= clock.getDelta() * 2 * Math.PI / period;
renderer.render(scene, camera);
}
html, body, #canvas {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script>
<canvas id="canvas"></canvas>
<script id="vertex-shader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
#define M_TAU 6.2831853071795864769252867665590
varying vec2 vUv;
void main() {
float linecount = 200.0;
float thickness = 0.0;
float blendregion = 2.8;
// Loosely based on https://github.com/AnalyticalGraphicsInc/cesium/blob/1.16/Source/Shaders/Materials/GridMaterial.glsl#L17-L34
float scaledWidth = fract(linecount * vUv.s);
scaledWidth = abs(scaledWidth - floor(scaledWidth + 0.5));
vec2 dF = fwidth(vUv) * linecount;
float value = 1.0 - smoothstep(dF.s * thickness, dF.s * (thickness + blendregion), scaledWidth);
gl_FragColor = vec4(value, 0.0, 0.0, 1.0);
}
</script>

Alpha color with ShaderMaterial

I am trying to write a shader material for displaying THREE.Points() objects with rounded points. I am doing this by controlling the alpha value in a fragment shader.
Below is a fully working version of my code. I am getting the desired effect of only colouring pixels within the circle. Outside, however, I am getting white instead of the background colour.
This question is related to mine. I tried setting material.transparent = true but it did not have any effect.
<html>
<head>
<title>THREEJS alpha shader</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
<script type="text/javascript" src="http://threejs.org/build/three.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
</head>
<body>
<!-- Shaders -->
<script type="x-shader/x-vertex" id="vertexshader">
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
gl_PointSize = 40.0; // pixels
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
varying vec4 color;
void main() {
// radius
float r = length(gl_PointCoord - vec2(0.5, 0.5));
if(r < 0.5) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else {
gl_FragColor = vec4(1.0, 1.0, 1.0, 0.0);
}
}
</script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var buffer = new Float32Array(3); // single point located at (0, 0, 0)
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(buffer,3) );
// create shader material
var material = new THREE.ShaderMaterial({
vertexShader : $('#vertexshader').text(),
fragmentShader : $('#fragmentshader').text(),
});
var point = new THREE.Points(geometry, material);
scene.add(point);
camera.position.z = 2;
var render = function () {
requestAnimationFrame( render );
renderer.render(scene, camera);
};
render();
</script>
</body>
</html>
You need to enable alpha blending within the OpenGL context. I am not sure if there is a way to do this using three.js, but adding these GL commands to your render() function will suffice:
var render = function () {
var gl = renderer.context;
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
requestAnimationFrame( render );
renderer.render(scene, camera);
};
See the MDN docs on blendFunc for a bit more information on this.

Can three.js render a translucent mesh among translucent particles?

I'm attempting to draw a translucent sphere among a cloud of translucent particles. The sphere is translucent while it's in front of the particles, but it turns opaque as soon as it's among the particles (ie, if a particle vertex sits between the camera and the sphere's surface, sphere becomes opaque). Check out the snippet below (click 'Full page', it looks much better).
Newer versions of three removed .sortParticles, which comes into play here, but I'm working around that by copying the sortPoints function from this example.
The depths of every particle and the sphere seem to be accurate, it's just that the opacity is lost. Perhaps blending fails under certain circumstances?
Is there a way to draw a translucent mesh among translucent particles?
var renderer, scene, camera, sphere;
var particleSystem, uniforms, geometry;
var particles = 200;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000);
camera.position.z = 70;
scene = new THREE.Scene();
uniforms = {
color: {
type: "c",
value: new THREE.Color(0xffffff)
},
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
depthTest: true,
depthWrite: false,
transparent: true
});
var sphere_geometry = new THREE.SphereGeometry(10, 32, 32);
var sphere_material = new THREE.MeshNormalMaterial();
sphere_material.transparent = true;
sphere_material.opacity = 0.6;
sphere_material.depthTest = true;
//sphere_material.depthWrite = false;
sphere = new THREE.Mesh(sphere_geometry, sphere_material);
//sphere.renderOrder = -1;
scene.add(sphere);
camera.lookAt(sphere.position);
var radius = 30;
geometry = new THREE.BufferGeometry();
var positions = new Float32Array(particles * 3);
var colors = new Float32Array(particles * 3);
var sizes = new Float32Array(particles);
var color = new THREE.Color();
for (var i = 0, i3 = 0; i < particles; i++, i3 += 3) {
positions[i3 + 0] = i - 50;
positions[i3 + 1] = i - 50;
positions[i3 + 2] = 2*i - 100;
color.setHSL(i / particles, 1.0, 0.5);
colors[i3 + 0] = color.r;
colors[i3 + 1] = color.g;
colors[i3 + 2] = color.b;
sizes[i] = 3000;
}
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));
particleSystem = new THREE.Points(geometry, shaderMaterial);
scene.add(particleSystem);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(WIDTH, HEIGHT);
var container = document.getElementById('container');
container.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
var time = Date.now() * 0.005;
var n = 30;
sphere.position.z = n * (1 + Math.sin(0.2 * time)) - n * 1.5;
sortPoints();
renderer.render(scene, camera);
}
function sortPoints() {
var vector = new THREE.Vector3();
// Model View Projection matrix
var matrix = new THREE.Matrix4();
matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
// matrix.multiply( particleSystem.matrixWorld );
//
var index = geometry.getIndex();
var positions = geometry.getAttribute('position').array;
var length = positions.length / 3;
if (index === null) {
var array = new Uint16Array(length);
for (var i = 0; i < length; i++) {
array[i] = i;
}
index = new THREE.BufferAttribute(array, 1);
geometry.setIndex(index);
}
var sortArray = [];
for (var i = 0; i < length; i++) {
vector.fromArray(positions, i * 3);
vector.applyProjection(matrix);
sortArray.push([vector.z, i]);
}
function numericalSort(a, b) {
return b[0] - a[0];
}
sortArray.sort(numericalSort);
var indices = index.array;
for (var i = 0; i < length; i++) {
indices[i] = sortArray[i][1];
}
geometry.index.needsUpdate = true;
}
body {
color: #ffffff;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #000000;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 0px;
width: 100%;
padding: 5px;
z-index:100;
}
<script src="http://threejs.org/build/three.min.js"></script>
<div id="info">translucent mesh amidst translucent particles</div>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size / length(mvPosition.xyz);
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(color * vColor, 0.2);
}
</script>
<div id="container"></div>

Resources