Replicating MeshLambertMaterial Using ShaderMaterial ignores textures
javascript, opengl-es, shader, three.js, webgl
Solution
three.js was designed to be easy to use, not easy to modify. This may change in the future...
You need to set the `material.defines` like so:
var defines = {};
defines[ "USE_MAP" ] = "";.
Then specify `defines` in the material constructor.
var material = new THREE.ShaderMaterial({
name: "TerrainShader",
defines : defines,
uniforms : shaderUniforms,
vertexShader: THREE.MyShader.vertexShader,
fragmentShader: THREE.MyShader.fragmentShader,
fog:false,
lights:true
});
Regarding the texture repetitions, you need to add the repeat to your uniforms:
shaderUniforms[ "offsetRepeat" ].value.set( 0, 0, 2, 2 );
three.js r.66
Problem
I noticed that THREE.js uses shaders internally to create core material "e.g. MeshLambertMaterial", So I decided to copy the lambert shader from Three.js code into a new shader and build on it. This is the code I got (Copied faithfully from Three.js r66) ``` THREE.MyShader = { uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ "common" ], THREE.UniformsLib[ "fog" ], THREE.UniformsLib[ "lights" ], THREE.UniformsLib[ "shadowmap" ], { "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } } ]), vertexShader: [ "#define LAMBERT", "varying vec3 vLightFront;", "#ifdef DOUBLE_SIDED", "varying vec3 vLightBack;", "#endif", THREE.ShaderChunk[ "map_pars_vertex" ], THREE.ShaderChunk[ "lightmap_pars_vertex" ], THREE.ShaderChunk[ "envmap_pars_vertex" ], THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], THREE.ShaderChunk[ "color_pars_vertex" ], THREE.ShaderChunk[ "morphtarget_pars_vertex" ], THREE.ShaderChunk[ "skinning_pars_vertex" ], THREE.ShaderChunk[ "shadowmap_pars_vertex" ], "void main() {", THREE.ShaderChunk[ "map_vertex" ], THREE.ShaderChunk[ "lightmap_vertex" ], THREE.ShaderChunk[ "color_vertex" ], THREE.ShaderChunk[ "morphnormal_vertex" ], THREE.ShaderChunk[ "skinbase_vertex" ], THREE.ShaderChunk[ "skinnormal_vertex" ], THREE.ShaderChunk[ "defaultnormal_vertex" ], THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], THREE.ShaderChunk[ "default_vertex" ], THREE.ShaderChunk[ "worldpos_vertex" ], THREE.ShaderChunk[ "envmap_vertex" ], THREE.ShaderChunk[ "lights_lambert_vertex" ], THREE.ShaderChunk[ "shadowmap_vertex" ], "}" ].join("\n"), fragmentShader: [ "uniform float opacity;", "varying vec3 vLightFront;", "#ifdef DOUBLE_SIDED", "varying vec3 vLightBack;", "#endif", 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[ "shadowmap_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" ], "#ifdef DOUBLE_SIDED", //"float isFront = float( gl_FrontFacing );", //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", "if ( gl_FrontFacing )", "gl_FragColor.xyz *= vLightFront;", "else", "gl_FragColor.xyz *= vLightBack;", "#else", "gl_FragColor.xyz *= vLightFront;", "#endif", 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") } ``` and this is the code that I use to setup my uniforms and create the material. ``` var textureUsed = 'rock_1'; var texture = THREE.ImageUtils.loadTexture( texturePath + textureUsed + "/diffuse.png"); texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.x = 128; texture.repeat.y = 128; var shaderUniforms = THREE.UniformsUtils.clone( THREE.MyShader.uniforms ); shaderUniforms[ "map" ].value = texture; var material = new THREE.ShaderMaterial({ name: "TerrainShader", uniforms : shaderUniforms, vertexShader: THREE.MyShader.vertexShader, fragmentShader: THREE.MyShader.fragmentShader, fog:false, lights:true }); ``` The problem is that when I use these parameters to create a MeshLambertMaterial I get the correct lighting and texture repetitions, when I use it to create ShaderMaterial the lights and shadows seem to be working but the texture map isn't loaded, to fix this I dug through the code and managed to get the map to load by adding this ugly "hack" to my code, right after the material definition ``` material.map = true; ``` Now the texture is loaded and displayed but it looks like the texture coordinates are messed up, instead of repeating, the Shader seems to be ignoring the repeat values I supplied. Why did I need that hack to get my texture processed and what can I do to get the correct texture repetitions?