问题
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?
回答1:
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
来源:https://stackoverflow.com/questions/21928178/replicating-meshlambertmaterial-using-shadermaterial-ignores-textures