Using lights in three.js shader - three.js

I'm trying to access the scene's lights from a shader in three.js.
This question is nearly a duplicate of Three.js ShaderMaterial issue with lights
but the comments on that question aren't helping me resolve the issue.
Here is the vertex shader:
#if NUM_DIR_LIGHTS > 0
struct DirectionalLight {
vec3 direction;
vec3 color;
int shadow;
float shadowBias;
float shadowRadius;
vec2 shadowMapSize;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
#endif
varying vec3 color;
void main() {
float r = directionalLights[0].color.r;
color = vec3(r,1.0,0.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position , 1.0);
}
and the relevant ShaderMaterial:
var material = new THREE.ShaderMaterial({
uniforms: THREE.UniformsLib['lights'],
vertexShader: document.getElementById('vertexShader').innerHTML,
fragmentShader: document.getElementById('fragmentShader').innerHTML,
lights : true
});
I've posted the entire example code here: https://jsfiddle.net/zhkvcajs/
Removing the lights : true renders a green knot, but it's not getting the directionalLights information that should change the knot's color. Apparently, lights : true is required for that, but causes an error.

If you want to use scene lights with ShaderMaterial, you need to set lights: true.
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').innerHTML,
fragmentShader: document.getElementById('fragmentShader').innerHTML,
lights: true
} );
three.js r.126

Related

Three.js ShaderMaterial artifacts

I tried a very simple test using Three.js ShaderMaterial.
I load a 2048x2048 jpg image as a texture for my height map and apply it to deform a PlaneBufferGeometry in the vertex shader.
I also apply the same texture for the diffuse color in the fragment shader.
Globally it works but I see some big artifacts as shown in this screenshot
The artifact always appears along a line parallel to the X axis and passing through the camera.
I have the problem on all three.js version I tried (r105, r114)
The code is yet very simple, anyone know what am I doing wrong ?
Javascript
var textureLoader = new THREE.TextureLoader();
var testTextureBump = textureLoader.load( './front_b.jpg' );
var testGeometry = new THREE.PlaneBufferGeometry(3000, 3000, 500, 500);
var testUniforms = {
uTextureBump: { value: testTextureBump }
};
var testMaterial = new THREE.ShaderMaterial({
uniforms: testUniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.FrontSide,
blending: THREE.NormalBlending,
depthWrite: false,
wireframe: false,
transparent: true
});
var testMesh = new THREE.Mesh( testGeometry, testMaterial );
scene.add( testMesh );
Vertex shader
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main() {
vUv = uv;
vec4 diffuseTexture = texture2D(uTextureBump, uv);
vec3 positionHeight = position.xyz;
positionHeight.z += diffuseTexture.r * 20.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(positionHeight, 1.0);
}
Fragment shader
precision highp float;
precision highp int;
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main (void) {
vec4 texture = texture2D(uTextureBump, vUv);
gl_FragColor = vec4( texture.rgb, 1.0 );
}
You can see the problem in this demo
Move your mouse on the left or right and you'll see the artifacts.
You can fly around as I use the standard THREE.FlyControl service.
The corresponding project file can be download here

THREE.JS ShaderMaterial exceeds of my mesh

I have a classic mesh composed by a THREE.PlaneGeometry and a material. If I add a THREE.MeshNormalMaterial() here's the result I get :
So far, so good. But when I call my THREE.ShaderMaterial(), using an external texture, the dimension of my mesh completely changes :
I always get that weird ratio even if - like in the screenshot - my texture is a square (512x512). I just want my MaterialShader to fit inside my geometry.
Here is the code of my MaterialShader :
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
I don't see what I'm missing. Does anyone has an idea ? Thank you very much.
UPDATE :
Here's the fully code of my ShaderMaterial :
material = new THREE.ShaderMaterial({
uniforms:{
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() },
texture1: { type: "t", value: texture }
},
vertexShader:`
void main() {
gl_Position = vec4( position, 1.0 );
}
`,
fragmentShader:`
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
uniform sampler2D texture1;
void main(){
float pyt=3.1415926*2./3.;
float m=-1e10;
vec4 mv= vec4(0.);
vec2 xy = gl_FragCoord.xy/u_resolution.xy;
int ic=0;
for (int i=0;i<30;i++){
vec2 np=vec2(xy.x+float(i)/u_resolution.x*sin(3.14/2.) * 4.,xy.y+float(i)/u_resolution.y*cos(3.14/2.) * 4.);
float jTime = u_time*1.618;
vec4 tk=texture2D(texture1,np);
float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);
if (t>m){m=t; mv=tk;ic=i;}
}
float sc=float(ic)/30.;
vec4 tk=texture2D(texture1,xy);
mv=sc*tk+(1.-sc)*mv;
gl_FragColor = vec4(mv.r,mv.g,mv.b,1.0);
}
`
});
UPDATE2 :
I changed my vertex shader but nothing has changed.
I might have a lead : I think this is related to my camera settings. I changed them and I've a better result. Now my texture fits into my square mesh.
Unfortunately, the scale isn't good. Since my texture is a square too, I want it to have exactly the same size than my mesh, for now it's zoomed.
How can I manage the size of my texture ? Should I do it inside my vertexShader ?
Here's my texture settings for now :
texture = new THREE.TextureLoader().load( "test5.jpg");
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
UPDATE 3 :
I found that code to apply a texture and make it fit to my mesh :
https://bl.ocks.org/duhaime/c8375f1c313587ac629e04e0253481f9
It's working but as soon as I change the example fragement shader by mine, I've no errors but the shaders become one unique color. I don't understand what I'm missing...
Try this code of the vertex shader:
void main() {
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
Reference
Simply pass the uv coordinates from the vertex shader to the fragment shader and use them there.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x404040);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var iResolution = new THREE.Vector2();
var planeGeo = new THREE.PlaneBufferGeometry(5, 5);
var planeMat = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: null
},
iResolution: {
value: iResolution
},
iTime: {
value: 0
}
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
`,
fragmentShader: `
uniform sampler2D texture;
uniform float iTime;
uniform vec2 iResolution;
varying vec2 vUv;
void main() {
float pyt=3.1415926*2./3.;
float m=-1e10;//very negitive start value for maximisation algorithm.
vec4 mv= vec4(0.);//lank starting value of max so far
vec2 xy = vUv;
int ic=0;//stores smear distance
for (int i=0;i<30;i++){
//point offset on a circle
vec2 np=vec2(xy.x+float(i)/iResolution.x*sin(iTime),xy.y+float(i)/iResolution.y*cos(iTime));
//colour cycles faster than position
float jTime = iTime*1.618;
//get neerby point
vec4 tk=texture2D(texture,np);
// and if its colourfull enough, use that
float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);
if (t>m){m=t; mv=tk;ic=i;}
}
//mix smeared with background depending ondistance
float sc=float(ic)/30.;
vec4 tk=texture2D(texture,xy);
mv=sc*tk+(1.-sc)*mv;
gl_FragColor = vec4(mv.rgb,1.0);
}
`
});
var textureLoader = new THREE.TextureLoader();
textureLoader.load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg", tex => {
planeMat.uniforms.texture.value = tex;
planeMat.uniforms.texture.value.needsUpdate = true;
iResolution.set(tex.image.width, tex.image.height);
planeMat.needsUpdate = true;
console.log(texture);
});
var plane = new THREE.Mesh(planeGeo, planeMat);
scene.add(plane);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
planeMat.uniforms.iTime.value = time;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>

ThreeJS draggable CSS3DObject

Been using THREE JS CSS3DRenderer to render a panorama view based in this example.
I have added another CSS3DObject image which represents a GPS marker with the following code below:
var marker = document.createElement( 'img' );
marker.classList.add('gps_marker');
marker.src = 'gps_marker.jpg';
marker.width = 5;
marker.style.border = 'solid 1px red';
var object = new THREE.CSS3DObject( marker );
object.position.set( 0, 0, 30 );
scene.add( object );
Now I'd like to make the gps markers draggable around the scene around the scene. Dragcontrols doesn't seem to work with CSS3DObject. Any help would be very appreciated.
You need to use "textured" point cloud (THREE.Points) object with shader material. Use uniform to pass texture to fragment shader. Not sure that dragcontrols will work with it (will move all points at ones if so). But this is IMHO the best way to draw icons. Example:
let uniforms = {
iconSize: {
value: self.iconSize
},
color: {
value: new THREE.Color(0xffffff)
},
texture: {
value: self.textureLoader.load(url, texture => {
texture.flipY = false;
texture.needsUpdate = true;
})
}
};
let material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('point-cloud-vertex-shader').textContent,
fragmentShader: document.getElementById('point-cloud-fragment-shader').textContent,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
depthTest: false,
clipping: true,
clippingPlanes: self.clippingPlanes,
});
//... create THREE.Points object with this material
shaders:
<script type="x-shader/x-vertex" id="point-cloud-vertex-shader">
uniform vec3 color;
uniform float iconSize;
uniform sampler2D texture;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = iconSize;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="point-cloud-fragment-shader">
uniform vec3 color;
uniform float iconSize;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D( texture, gl_PointCoord );
}
</script>

How to fit noise shader into my plane geometry

The following link is my pen:
https://codepen.io/johnhckuo/pen/RxrXxX
And here is my code in fragment shader:
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 worldtoEye = eye - worldPosition;
vec3 eyeDirection = normalize(worldtoEye);
vec3 pos = vec3(st.x*5.0, st.y*5.0, u_time*0.5);
color = vec3(fbm(pos) + 1.0);
//color = vec3(noise(pos)*0.5 + 1.0);
vec3 sunLight = vec3(1., 1., 1.);
color *= (diffuseLight(sunLight) + specularLight(eyeDirection));
vec3 oceanBlue = vec3(0.109, 0.419, 0.627);
gl_FragColor = vec4(oceanBlue * color, 1.0);
}
Here is the code of plane geometry:
var plane_geometry = new THREE.PlaneBufferGeometry( 2000, 2000, 32 );
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
}
);
customMaterial.side = THREE.DoubleSide;
var surface = new THREE.Mesh( plane_geometry, customMaterial );
surface.position.set(0,0,0);
scene.add( surface );
I've created a shader and tried to apply it onto my plane.
But whenever I zoom in/out, the shader since to be fixed to the screen and not zooming in/out correspondingly.
Any suggestions is appreciated !
Change this line:
vec2 st = gl_FragCoord.xy/u_resolution.xy;
to
vec2 st = uVu.xy* 2.0;
and fiddle with 2.0 (the resolution)
It's also easy to test out vert + frag shaders on ShaderFrog, as in: https://shaderfrog.com/app/view/1997

Using FBO particles with a first person camera in Three.js

I am attempting to make a first person experiment incorporating FBO particles (similar to Mr. Doob's sporel.
I create the position of my particles in a data texture, and then in my vertex shader I do a lookup on the texture to get the position of that particle.
uniform sampler2D map;
varying vec2 vUv;
varying vec3 vPosition;
void main(void) {
vec2 uv = position.xy;
vec4 data = texture2D(map, uv);
vPosition = data.xyz;
gl_PointSize = 1.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
This is working great, and initially I see a field of particles as shown here...
The problem is when I walk a bit and rotate, all of the particles suddenly disappear. I did a bunch of searching around for a solution, and found this thread which suggested setting material.depthWrite to false. I did this, to no avail- here is my ShaderMaterial definition:
var particleMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shaders.vertexShaders.floor,
fragmentShader: shaders.fragmentShaders.floor,
blending: THREE.AdditiveBlending,
depthWrite: false,
})
I am at a loss right now and any help would be greatly appreciated!
Thanks! Eric

Resources