Three.js ShaderMaterial with lights on mesh imported from Blender - three.js

I'm trying to get a ShaderMaterial with lights working in three.js r77. It works correctly when applied to a mesh with a simple BoxGeometry but it behaves incorrectly when applied to a mesh imported from Blender.
A simple jsfiddle illustrating the problem is here. The mesh on the left is created from a Blender export. The mesh on the right is created from a simple BoxGeometry. Both are using the same ShaderMaterial. The position of the light is indicated by the DirectionalLightHelper.
The mesh on the right is being lit correctly, while the mesh on the left is not. Clearly the problem is in my shader code. I initially assumed that the problem is in the UV map on the imported mesh, but that does not appear to be true. In the jsfiddle example the UV map is copied directly to the BoxGeometry mesh from the imported geometry---they're rotated relative to each other because of the coordinate differences between Blender and three.js, but the lighting is still working correctly on the mesh on the right with the imported UVs.
The shader code is:
THREE.TestShader = {
uniforms: {
"uDirLightPos": {
type: "v3",
value: new THREE.Vector3(20, 20, 20)
},
"uDirLightColor": {
type: "c",
value: new THREE.Color(0xffffff)
},
"uTexture": {
type: "t",
value: null
},
},
vertexShader: [
"varying vec3 vNormal;",
"varying vec3 vViewPosition;",
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"vNormal = normalize(normalMatrix * normal);",
"vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);",
"vViewPosition = -mvPosition.xyz;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 uDirLightPos;",
"uniform vec3 uDirLightColor;",
"varying vec2 vUv;",
"varying vec3 vNormal;",
"varying vec3 vViewPosition;",
"varying vec4 mvPosition;",
"uniform sampler2D uTexture;",
"void main() {",
"vec4 lDirection = viewMatrix * vec4(uDirLightPos, 0.0);",
"vec3 lVector = normalize(lDirection.xyz);",
"vec3 normal = normalize(vNormal);",
"float diffuse = dot(normal, lVector);",
"vec4 texel = texture2D( uTexture, vUv );",
"gl_FragColor = vec4(uDirLightColor * diffuse, 1.0) * texel;",
"}"
].join("\n")
};
In addition to a solution to this particular problem, a pointer to better documentation on the three.js shader stuff would be appreciated. The official documentation for ShaderChunk, ShaderLib, and UniformsLib is not what you'd call exhaustive.

Your Blender-exported model has incorrect normals.
vnh1 = new THREE.VertexNormalsHelper( mesh1, 1, 0xff0000, 1 );
scene.add( vnh1 );
fiddle: https://jsfiddle.net/5j0axcgz/1/
three.js r.77

Related

Three.js Cubemap reflection shader rotation

I'm trying to write my own reflection shader however the reflection does not work correctly when I try to rotate the sphere. It works only when my sphere is static. Here is my GLSL code:
sphereMaterial = new THREE.ShaderMaterial({
uniforms: {
uCubeMap: { value: cubeCamera.renderTarget.texture },
},
vertexShader: `
varying vec2 vUv;
varying vec3 vViewVector;
varying vec3 vNormal;
void main() {
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;
vViewVector = modelPosition.xyz - cameraPosition;
vNormal = normal;
gl_Position = projectedPosition;
}
`,
fragmentShader: `
uniform samplerCube uCubeMap;
varying vec3 vViewVector;
varying vec3 vNormal;
void main() {
vec3 reflectedDirection = normalize(reflect(vViewVector, vNormal));
reflectedDirection.x = -reflectedDirection.x;
vec3 textureColor = textureCube(uCubeMap, reflectedDirection).rgb;
gl_FragColor = vec4(textureColor, 1.0);
}
`,
});
Complete example: https://codepen.io/marianban/pen/VwPeYer?editors=0010 The sphere rotation is on the line 143 sphereGroup.rotation.x += 0.01;. It works fine if you comment it. It looks like that the reflection is not taking the sphere rotation into account.
I found several similar questions: GLSL cubemap reflection shader, Texturing Spheres with Cubemaps (not reflection maps) but unfortunately they didn't help me.

How to make Three.js ShaderMaterial transparent

I'm very new to shaders. I'm trying to achieve this color and transparent effect:
Example #1 Example#2
This is my result:
my result
This what I have so far:
this.material = new THREE.ShaderMaterial({
extensions: {
derivatives: "#extension GL_OES_standard_derivatives : enable"
},
side: THREE.DoubleSide,
uniforms: {
time: { value: 0 },
resolution: { value: new THREE.Vector4() }
},
transparent: true,
vertexShader: vertex,
fragmentShader: fragment
});
fragmentShader
varying vec2 vUv;
varying float vNoise;
uniform vec2 u_resolution;
void main() {
vec3 color1 = vec3(0.,0.,0.);
vec3 color2 = vec3(1.,1.,1.);
vec3 finalcolor = mix(color1,color2,0.9*(vNoise+1.));
gl_FragColor = vec4( vec3(finalcolor),0.2);
}
How would you go about this?
Thanks!
I guess that it is a bit late for answer, but maybe someone will have same problem. I think you are looking for Fresnel Shader:
Three.js example can be found here:
https://jsfiddle.net/8n36c47p/4/
Here is main shader part:
vertexShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vPositionW = vec3( vec4( position, 1.0 ) * modelMatrix);",
" vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );",
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vec3 color = vec3(1., 1., 1.);",
" vec3 viewDirectionW = normalize(cameraPosition - vPositionW);",
" float fresnelTerm = dot(viewDirectionW, vNormalW);",
" fresnelTerm = clamp(1.0 - fresnelTerm, 0., 1.);",
" gl_FragColor = vec4( color * fresnelTerm, 1.);",
"}"
].join( "\n" )

gradient multicolor metaballs with threejs and marchingcubes

I am looking on how to implement something similar than this work: https://vimeo.com/9121195 . But with explicitly attributing colors to each metaball from an array of given colors. As far as I can see, this is done entirely from the shader side in this example but I was wondering if this could not be implemented with Threejs and marchingcubes.
By creating a new THREE.ShaderMaterial I assume I can pass positions of each metaball to the shader and then evaluate the distance of each vertex with the given positions to finally assign a color to each pixel.
Just to simplify the problem I start with two moving metaballs.
from threejs:
function init() {
//...
var strength = 0.61;
var resolution = 70;
var substract = 25;
var scaleFactor = 1200;
var posData = []; // array storing metaballs positions
var myShaderMaterial = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
name:"myShaderMaterial" }); // shader is described below
effect = new THREE.MarchingCubes( resolution, myShaderMaterial,false,true);
effect.position.set( 0, 0, 0 );
effect.scale.set( scaleFactor, scaleFactor, scaleFactor );
effect.enableUvs = true;
effect.enableColors = true;
effect.hasColors = true;
effect.castShadow = true;
//...
scene.add( effect );
}
function render() {
effect.reset();
effect.init(resolution);
for (var i = 0; i <= posData.length - 1; i++) {
item = posData[i];
effect.addBall(item[0], item[1], item[2], strength, substract);
effect.material.uniforms.allPos.value[i] = new THREE.Vector3(item[0],item[1],item[2]).multiplyScalar(1./scaleFactor);
}
//...
}
here is my shader:
'myShaderMaterial' : {
uniforms: {
"colChoice":{type: "v3v", value:[new THREE.Color( 0xef5350 ),
new THREE.Color( 0xffffff )]},
"allPos":{type: "v3v",value:[new THREE.Vector3(0., 0., 0.),
new THREE.Vector3(0., 0., 0.)]},
},
vertexShader: [
"uniform vec3 colChoice[2];",
"varying vec3 vNormal;",
"varying vec3 vRefract;",
"varying vec3 blobColor;",
"varying vec3 otherColor;",
"uniform vec3 allPos[2];",
"varying float mixScale;",
"void main() {",
"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"vec3 worldNormal = normalize ( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );",
"vNormal = normalize( normalMatrix * normal );",
"gl_Position = projectionMatrix * mvPosition;",
"float distMin = 100000000000000000000000000000000000000.;",
"float distMax = 0.;",
"for (int i=0;i<2;i++){",
"float distV = distance(allPos[i], position );",
"if (distV<distMin){",
"distMin = distV;",
"}",
"if (distV>distMax){",
"distMax = distV;",
"}",
"mixScale = smoothstep(distMin,distMax,distV);",
"if (mod(float(i),2.0)==0.){",
"blobColor = colChoice[0];",
"otherColor = colChoice[1];",
"}",
"else{",
"blobColor = colChoice[1];",
"otherColor = colChoice[0];",
"}",
"}",
"}",
].join( "\n" ),
fragmentShader: [
"varying vec3 blobColor;",
"varying vec3 otherColor;",
"varying vec3 vNormal;",
"varying float mixScale;",
"void main() {",
"vec3 finalColor = (0.3*vNormal) + mix(otherColor,blobColor,mixScale);",
"gl_FragColor = vec4(finalColor,1.0);",
"}",
].join( "\n" )
here is a sample result:
The issue here is that obviously the distance is not calculated properly and the transition between two colors that I tried to do with mixScale also does not work. Any idea?
Yeah it's possible but I don't think it does it out of the box. I peeked at the marchingcubes code you linked, and it has an "enableColor" flag but color seems to be filled with the vertex coordinate instead of interpolating a separate color value from the density field.
I've had to do something very similar in the past to do multi material terrain generation from noise fields. I think I ended up extending the marching cubes to interpolate colors, then assigned each material to a color channel, r, g, b.

Same texture offseting different position in the fragment shader using threejs rendering engine

My vertex shader:
varying vec2 texCoord;
void main() {
texCoord = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
My fragment shader:
varying vec2 texCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float multiplier;
void main( void ) {
vec3 tex1 = texture2D(texture1, texCoord).xyz ;
vec3 tex2 = texture2D(texture2, texCoord).xyz ;
vec3 finaltex = mix( tex1, tex2, multiplier) ;
gl_FragColor = vec4(finaltex , 1.0);
}
Now this work very well when i run using the two texture.check http://shaderfrog.com/app/view/68 for the multiplier action.
But now what I want is I am having texture like this:
So with the single texture I want to index the offset of my texCoord so that I just need to sample one texture and I can get three representation form this like:
var shaderMaterial = new THREE.ShaderMaterial({
uniforms:{
texture1: { type: "t", value: texture1 }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
});
Can offset my tri-color in the fragment shader. or Some one can help me modifying the fragment shader so that I can pass uniform to index my tri-color into individual Yellow,pink,red.
So either from shader or threejs I can get help regarding same.
I have done reference using two texture because I want to interpolate with cross fade effect on the texture same I want cross fade using fragment shader (independent to this I have already achieve by the texture.offset.x = currentColoum / horizontal and texture.offset.y = currentRow / Vertical;
I found the answer to this question, even implemented into application :D.
vertexShader:
void main() {
texCoord = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
FragmentShader:
varying vec2 texCoord;
uniform sampler2D texture;
uniform vec2 offset_current;
uniform vec2 offset_next;
uniform float multiplier;
void main( void ) {
vec2 offset1 = offset_current + vec2( texCoord.x* 1.0, texCoord.y * 0.333333);
vec2 offset2 = offset_next + vec2( texCoord.x* 1.0, texCoord.y * 0.333333);
vec3 tex1 = texture2D(texture1,offset1).xyz ;
vec3 tex2 = texture2D(texture1, offset2).xyz ;
vec3 mixCol = mix( tex1, tex2, multiplier );
vec4 fragColor = vec4(mixCol, 1.0);
if(fragColor.a == 0.0)
discard;
gl_FragColor = fragColor;
}
Explanation:
Since I am having the vertical texture of three different type I make my offset into y direction 0.3333. Because texture is read from [0,1]. I have extended this code same for the horizontal direction.
If some one going to make this dynamic then instead of hard coded we can pass the 0.3333 as the calculate one taking the inspiration form link.

Normalize function in webGL not working (THREE.js)

I am currently working on creating a shader in THREE.JS which will act like the normal shader, but using a color as the input to define how it is shaded.
Below is the Code that is causing the problem, and after that is a longer explanation of why I am doing it, and what I think is causing the problem:
fragmentShader: [
"uniform float opacity;",
"varying vec3 vNormal;",
"uniform vec3 color;",
"void main() {",
//THIS IS WHAT I THINK IS SCREWING UP EVERYTHING
//Can I not call normalize with that complex
//of equations within a normalize function?
"gl_FragColor = vec4( normalize(color.r + (vNormal.x*color.r)*.5, color.g + (vNormal.y*color.g)*.5, color.b + (vNormal.z*color.b)*.5), opacity );",
"}"
].join("\n")
A longer description:
I am basically using the same code as the normal shader in the THREE.shaderLib, which is as follows:
'normal': {
uniforms: {
"opacity" : { type: "f", value: 1.0 }
},
vertexShader: [
"varying vec3 vNormal;",
"void main() {",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"vNormal = normalize( normalMatrix * normal );",
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader: [
"uniform float opacity;",
"varying vec3 vNormal;",
"void main() {",
"gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
"}"
].join("\n")
},
What I am using is basically this, but with a color aspect added, and it is called within a function that defines a new shader, like so:
function assignNewShader(color){
var colorAssign = new THREE.Color(color)
THREE.ShaderLib[color] = {
uniforms: {
"opacity" : { type: "f", value: 1.0 },
"color" : { type: "c", value: colorAssign },
},
vertexShader: [
"varying vec3 vNormal;",
"void main() {",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"vNormal = normalMatrix * normal;",
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"),
fragmentShader: [
"uniform float opacity;",
"varying vec3 vNormal;",
"uniform vec3 color;",
"void main() {",
"gl_FragColor = vec4(color.r + (vNormal.x*color.r)*.5, color.g + (vNormal.y*color.g)*.5, color.b + (vNormal.z*color.b)*.5, opacity );",
"}"
].join("\n")
}
}
You can see that the biggest difference lies within the 'fragmentShader' section, where the vNormal is used to make the gl_FragColor similar (but not exactly the same) as the color that is given in the function.
My problem is this: As a object is 'scaled' this color difference gets more and more drastic, to the point where all of the colors are only as bright as possible. Because of that, I tried to do the following to the 'fragementShader' section of the code:
fragmentShader: [
"uniform float opacity;",
"varying vec3 vNormal;",
"uniform vec3 color;",
"void main() {",
"gl_FragColor = vec4( normalize(color.r + (vNormal.x*color.r)*.5, color.g + (vNormal.y*color.g)*.5, color.b + (vNormal.z*color.b)*.5), opacity );",
"}"
].join("\n")
When I do this, I am greeted with a PLETHORA of errors including:
ERROR: 0:37: 'normalize' : no matching overloaded function found
ERROR: 0:37: 'constructor' : not enough data provided for construction
WebGL: INVALID_VALUE: attachShader: no object or object deleted
Could not initialise shader
VALIDATE_STATUS: false, gl error [1281]
WebGL: INVALID_OPERATION: getUniformLocation: program not linked
I am definitly in over my head getting into the webGL part of THREE, but it seems to me that this mode of altering the fragment shader should work. Does anybody have any ideas as to why it might not?
Normalize takes a vec3 not a vec4.

Resources