Custom ShaderMaterial map issue - three.js

I am trying to create a custom ShaderMaterial reusing the ShaderChunks but seem to have a problem when setting the map parameter.
In my custom shader, when I load a texture and set the materials map and uniforms.map.value value to the texture it does not get the values from the texture in my shader, though it does seem to set the USE_MAP definition.
HERE is a fiddle
In the shader, it turns green if USE_MAP is defined, but it should also get the map value.
Am I missing something simple?
Below are the important bits :)
Set up the shader
var shaderUniforms = THREE.UniformsUtils.merge([THREE.UniformsLib[ "common" ]]);
var shaderVertex = [
//THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "uv_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "begin_vertex" ],
THREE.ShaderChunk[ "project_vertex" ],
"}",
].join("\n");
var shaderFragment = [
THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "uv_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
"void main() {",
"vec4 diffuseColor = vec4(1.0, 0.0, 0.0, 1.0);",
"#ifdef USE_MAP",
' diffuseColor = vec4(0.0, 1.0, 0.0, 1.0);',
'#endif',
THREE.ShaderChunk[ "map_fragment" ],
"gl_FragColor = diffuseColor;",
"}",
].join("\n");
Create the material
material = new THREE.ShaderMaterial({
uniforms: shaderUniforms,
vertexShader: shaderVertex,
fragmentShader: shaderFragment
});
Add the texture
function loadTexture() {
var loader = new THREE.TextureLoader();
loader.load(dataurl, function(texture){
console.log('texture loaded ok');
// What to do here to add texture (map) and update ShaderMaterial?
// Do I have to set needsUpdate on the texture?
texture.needsUpdate = true;
// Do I have to set needsUpdate on the material?
material.needsUpdate = true;
// Seems I have to set both the uniform map value and the material map value...
material.uniforms.map.value = texture;
material.map = texture;
// But still does not show the texture, though USE_MAP does seem to get defined in the shader (color changes to green).
// Hmmmm
});
}

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 Shader material how get (map) texture?

I have one problem here)
I decided to use my shader for (scene.overrideMaterial) and I need to somehow get the map texture.
MaterialsShader = {
uniforms: {
time: { type: 'f', value: 0.0 },
},
vertexShader:
"varying vec2 vUv; \n\
void main(){\n\
vUv = uv;\n\
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );\n\
gl_Position = projectionMatrix * mvPosition;\n\
}",
fragmentShader: [
'varying vec2 vUv;',
'uniform float opacity;',
'#ifdef USE_MAP',
'uniform sampler2D map;',
'#endif',
'void main(){',
'vec3 color = vec3(1.0,0.0,0.0) * opacity;',
'#ifdef USE_MAP',
'vec4 mapTexel = texture2D( map, vUv.xy );',
'gl_FragColor = mapTexel;',
'#endif',
//'gl_FragColor.rgb = color;',
'}'].join("\n")
}
But for some reason the texture itself does not exist, I get black material.
var uniforms = THREE.UniformsUtils.clone( MaterialsShader.uniforms );
uniforms = THREE.UniformsUtils.merge( [uniforms, THREE.UniformsLib['common'],THREE.UniformsLib['lights']] );
var material = new THREE.ShaderMaterial({
uniforms : uniforms,
vertexShader : MaterialsShader.vertexShader,
fragmentShader : MaterialsShader.fragmentShader,
});
How can I get a (map) texture?
1) You need to actually define your map uniform...
MaterialsShader = {
uniforms: {
time: { type: 'f', value: 0.0 },
map: { type: 't', value: yourLoadedTexture },
},
2) You need to define USE_MAP in order to enter that code segment of your shader. Either add this to the top of your shader code:
#define USE_MAP true
Or you can set a defines property for your shader:
var material = new THREE.ShaderMaterial({
uniforms : uniforms,
vertexShader : MaterialsShader.vertexShader,
fragmentShader : MaterialsShader.fragmentShader,
map : yourLoadedTexture,
defines : {
USE_MAP: true
}
});
These have the same effect.

Three.js - Put two materials on the same object

I was wondering how to put two materials on the same object...
I have a shader material that I use for the colors, but I also want to have a basing lambert shading.
Here is the code :
var material1 = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var material2 = new THREE.MeshLambertMaterial({
color: 0xffffff,
});
var materials = [material1, material2];
var group = new THREE.Object3D();
var plane = new THREE.Mesh(geometry, materials);
Can you help me ?
Here is a fiddle that shows how to incorporate the lambert chuck into your own shader.
Is this what you wanted?
It calculates in the vertex shader the same as the THREE.MeshLambertMaterial.
var myShader = {
uniforms : THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
color : {type:"c", value:new THREE.Color(0x0000ff)}
}
]),
vertexShader : [
THREE.ShaderChunk[ "common" ],
'varying vec3 vLightFront;',
THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
'void main() {',
' vec3 transformedNormal = normalMatrix * normal;',
' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
THREE.ShaderChunk[ "lights_lambert_vertex" ],
' gl_Position = projectionMatrix * mvPosition;',
'}'
].join('\n'),
fragmentShader : [
'uniform vec3 color;',
'varying vec3 vLightFront;',
'void main() {',
' vec3 outgoingLight = color.rgb + vLightFront;',
' gl_FragColor = vec4(outgoingLight, 1.0);',
'}',
].join('\n'),
};
material = new THREE.ShaderMaterial({
uniforms : myShader.uniforms,
vertexShader : myShader.vertexShader,
fragmentShader : myShader.fragmentShader,
lights: true
});

Three.js material texture and color

I trying to make a material for a car model with three.js where the base color of the car can be changed dynamically. This was easy enough by changing the color attribute of the MeshPhongMaterial that I am using.
I then needed to apply a texture over the top and thought this would be easy enough by adding an image to the map attribute of the material.
The outcome was not what I expected though, the map/texture image was also shaded the color set by the color attribute. I want it to basically overlay the base color.
Can anyone point me in the right direction?
Here is a complete script chunk. Insert immediately after reading three.js
<script>
THREE.ShaderChunk.map_fragment = [
"#ifdef USE_MAP",
"vec4 texelColor = texture2D( map, vUv ); /* NEWWW */",
"#ifdef GAMMA_INPUT",
"texelColor.xyz *= texelColor.xyz;",
"#endif",
"gl_FragColor.rgb = mix(gl_FragColor.rgb,texelColor.rgb,texelColor.a);",
"vec3 surfDiffuse = mix(diffuse,vec3(1,1,1),texelColor.a);",
"#else",
"vec3 surfDiffuse = diffuse;",
"#endif"].join('\n');
// now replace references to 'diffuse' with 'surfDiffuse'
THREE.ShaderChunk.lights_phong_fragment =
THREE.ShaderChunk.lights_phong_fragment.replace(/\bdiffuse\b/gm,'surfDiffuse')
THREE.ShaderLib.phong.fragmentShader = [
"uniform vec3 diffuse;",
"uniform float opacity;",
"uniform vec3 ambient;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
THREE.ShaderChunk[ "color_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
THREE.ShaderChunk[ "lightmap_pars_fragment" ],
THREE.ShaderChunk[ "envmap_pars_fragment" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
THREE.ShaderChunk[ "bumpmap_pars_fragment" ],
THREE.ShaderChunk[ "normalmap_pars_fragment" ],
THREE.ShaderChunk[ "specularmap_pars_fragment" ],
"void main() {",
"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
THREE.ShaderChunk[ "map_fragment" ],
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
THREE.ShaderChunk[ "lights_phong_fragment" ],
THREE.ShaderChunk[ "lightmap_fragment" ],
THREE.ShaderChunk[ "color_fragment" ],
THREE.ShaderChunk[ "envmap_fragment" ],
THREE.ShaderChunk[ "shadowmap_fragment" ],
THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join('\n');
</script>
learned a little bit about the three.js phong material -- it doesn't handle highlights the way I'd like, but... whatever. If you need more sophistication I'd suggest using ShaderMaterial instead.
You will need a custom shader for that -- and a texture with an alpha channel (essentially, a DDS format). If you look in the three.js source, you'll see a bit of fragment-shader code called 'map_fragment' that looks like so:
"#ifdef USE_MAP",
"#ifdef GAMMA_INPUT",
"vec4 texelColor = texture2D( map, vUv );",
"texelColor.xyz *= texelColor.xyz;",
"gl_FragColor = gl_FragColor * texelColor;",
"#else",
"gl_FragColor = gl_FragColor * texture2D( map, vUv );",
"#endif",
"#endif"
As you can see it multiplies your texture map by the color (which at that point is in "gl_FragColor"). Instead, it sound like you want your map to overlay on top of the base color, only were you've painted it (say, chrome bumpers and headlights).
A simple way to do this might be to just alter the way 'phong' works before you instantiate any phong materials, by changing the value of that "map_fragment" string.
Instead of "gl_FragColor * (something)" -- try this as a re-assignment of THREE.ShaderChunk.map_fragment:
[
"#ifdef USE_MAP",
"vec4 texelColor = texture2D( map, vUv );",
"#ifdef GAMMA_INPUT",
"texelColor.xyz *= texelColor.xyz;",
"#endif",
"gl_FragColor = vec4(mix(gl_FragColor.rgb,texelColor.rgb,texelColor.a),(gl_FragColor.a*texelColor.a));",
"#endif"
].join('\n');
The hazard with overriding the stock method this way is that it will apply to ALL models in the scene -- the painted texture will overlay the material color. If that's okay, you're done. Otherwise you should create a new THREE.ShaderMaterial that's like "phong" except for the difference cited here.

Phong ShaderMaterial parameters / setting Defines

I'm currently customizing the base phong material using THREE.ShaderMaterial and rebuilding the material with most of THREE js's base chunks and a customized fragment shader chunk. The problem I'm having is with the #define's in many part of three.js and trying to find the proper way to set them.
In the actual program it goes like this
// Clone the uniforms
var uniforms = THREE.UniformsUtils.clone(shader['uniforms']);
// Set uniform values
uniforms["map"].value = texture;
uniforms["diffuse"].value = new THREE.Color( 0xff0000 );
uniforms["envMap"].value = envMapt;
uniforms["reflectivity"].value = 0.7;
// Create material using shader
var material = new THREE.ShaderMaterial( {
vertexShader: shader['vertexShader'],
fragmentShader: shader['fragmentShader'],
uniforms: uniforms,
lights: true,
//map: true, // These don't seem to do anything
//envMap: true // These don't seem to do anything
} );
With a custom shader built like this
fragmentShader: [
"#define USE_MAP",
//"#define USE_ENVMAP",
"uniform vec3 diffuse;",
"uniform float opacity;",
.......
"void main() {",
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
......
// NDJ - Using custom frag shader
//THREE.ShaderChunk[ "lights_phong_fragment" ],
CustomShaderChunk[ "lights_phong_fragment" ],
......
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
By manually adding the required #defines at the start of the shaders I can get it to do what I want. However this doesn't seem like the proper way to set it and it isn't very flexible.
Something similar to this, But I only need the base definitions. I've tried looking through the api and examples to find how to set these but can't seem to get it to work.
Just do it exactly as you described. Here is a sample:
ph = new THREE.MeshPhongMaterial( { ambient: 0x000000,
color: 0x0020ff,
specular: 0x2040ff,
shininess: 30,
map: theMap,
side: THREE.DoubleSide } );
ph.defines = {waldo_waldo_three: '(dx+3)', wonke: 7};
if you're being prudent, be careful about overwriting any exsiting 'defines' object

Resources