ThreeJS [r85]:带有阴影贴图的自定义着色器
ThreeJS [r85]: Custom Shader with Shadowmap
我创建了一个自定义着色器,以便能够使用具有 4 种不同纹理的 BlendMap,但我无法使用它来获得 shadows/lightning 效果。
我在这里错过了什么?或者有其他方法可以实现相同的功能吗?
下面的网格创建方法显示了正确混合的所有纹理。
// Creates the ground
function CreateGround() {
var uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib["lights"],
THREE.UniformsLib["shadowmap"],
{
TextureBackground: { type: "t", value: null },
TextureR: { type: "t", value: null },
TextureG: { type: "t", value: null },
TextureB: { type: "t", value: null },
TextureBlendMap: { type: "t", value: null }
}]);
var shaderMaterial;
try {
shaderMaterial = new THREE.ShaderMaterial({
lights: true,
uniforms: uniforms,
vertexShader: BlendMapVertexShader,
fragmentShader: BlendMapFragmentShader
});
} catch (e) {
alert("Error 'CreateGround' : GPU Shader couldn't compile");
}
shaderMaterial.uniforms.TextureBlendMap.value = _TextureBlendMap;
shaderMaterial.uniforms.TextureBackground.value = _TextureSand;
shaderMaterial.uniforms.TextureR.value = _TextureClay;
shaderMaterial.uniforms.TextureG.value = _TextureClay;
shaderMaterial.uniforms.TextureB.value = _TextureRock;
var geometry = new THREE.BoxGeometry(GROUND_SIZE, GROUND_HEIGHT, GROUND_SIZE);
var mesh = new THREE.Mesh(geometry, shaderMaterial);
mesh.castShadow = false;
mesh.receiveShadow = true;
return mesh;
}
这是我当前的着色器:
BlendMapVertexShader = [
THREE.ShaderChunk["shadowmap_pars_vertex"],
"varying vec2 vUv;",
"varying vec3 vPosition;",
"void main( void ) {",
"vUv = uv;",
"vPosition = position;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);",
THREE.ShaderChunk["begin_vertex"],
THREE.ShaderChunk["worldpos_vertex"],
THREE.ShaderChunk["shadowmap_vertex"],
"}",
].join("\n");
BlendMapFragmentShader = [
THREE.ShaderChunk["common"],
THREE.ShaderChunk["packing"],
THREE.ShaderChunk["shadowmap_pars_fragment"],
"varying vec2 vUv;",
"varying vec3 vPosition;",
"uniform sampler2D TextureBlendMap;",
"uniform sampler2D TextureBackground;",
"uniform sampler2D TextureR;",
"uniform sampler2D TextureG;",
"uniform sampler2D TextureB;",
"void main() {",
"vec4 cBlend = texture2D(TextureBlendMap, vUv);",
"float bText = 1.0 - (cBlend.r + cBlend.g + cBlend.b);",
"vec2 tiledCoords = vUv * 40.0;",
"vec4 cBack = texture2D(TextureBackground, tiledCoords) * bText;",
"vec4 cR = texture2D(TextureR, tiledCoords) * cBlend.r;",
"vec4 cG = texture2D(TextureG, tiledCoords) * cBlend.g;",
"vec4 cB = texture2D(TextureB, tiledCoords) * cBlend.b;",
"vec4 cTot = cBack + cR + cG + cB;",
"gl_FragColor = cTot;",
THREE.ShaderChunk["shadowmap_fragment"],
"}",
].join("\n");
我在浏览器中没有错误或警告。
three.js r85Dev
这是一个长镜头,但如果场景中的所有对象都使用此 material,则不应有任何阴影,因为没有任何东西会投射阴影。
mesh.castShadow = false;
mesh.receiveShadow = true;
但我猜测在您的其余代码中,您将一些网格设置为投射阴影。
我不知道我是否完全理解 ShaderChunk 方法,但它看起来只是一系列定义的字符串,您将其放入自己的文本中间,这部分看起来有点可疑:
"gl_FragColor = cTot;",
THREE.ShaderChunk["shadowmap_fragment"],
但我看不出 shadowmap_fragment 所做的任何事情如何有机会与我们现有的 gl_FragColor 值混合。在 shadowmap_fragment src 它运行:
void main() {
gl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );
}
其中 getShadowMask() 对 return 浮动的灯光做了很多魔术。所以我看不出您分配的 gl_FragColor 是如何与 shadowmap_fragment return 混合在一起的。换个顺序怎么样:
THREE.ShaderChunk["shadowmap_fragment"],
"gl_FragColor = cTot*gl_FragColor.a;"
不确定这是否有帮助,但您可以查看 shadowmap_pars_frag 的 src 以了解幕后发生的事情。也许我们遗漏了什么。
最后一件事:尝试只使用 none 纹理混合的阴影来隔离问题。可能是您达到了纹理采样数量的 GLSL 限制 - 因为阴影块也使用纹理采样。
好的。我最终复制了 PHONG 着色器,然后覆盖了漫反射颜色输入。解决方案如下。
这是用blendmapshader创建网格的方法
// Creates the ground
function CreateGround(material) {
var uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib["common"],
THREE.UniformsLib["aomap"],
THREE.UniformsLib["lightmap"],
THREE.UniformsLib["emissivemap"],
THREE.UniformsLib["bumpmap"],
THREE.UniformsLib["normalmap"],
THREE.UniformsLib["displacementmap"],
THREE.UniformsLib["gradientmap"],
THREE.UniformsLib["fog"],
THREE.UniformsLib["lights"],
{
emissive: { type: "c", value: new THREE.Color(0x000000) },
specular: { type: "c", value: new THREE.Color(0x111111) },
shininess: { type: "f", value: 30 },
TextureBackground: { type: "t", value: null },
TextureR: { type: "t", value: null },
TextureG: { type: "t", value: null },
TextureB: { type: "t", value: null },
TextureBlendMap: { type: "t", value: null },
}]);
var shaderMaterial;
try {
shaderMaterial = new THREE.ShaderMaterial({
lights: true,
uniforms: uniforms,
vertexShader: BlendMapVertexShader,
fragmentShader: BlendMapFragmentShader
});
} catch (e) {
alert("Error 'CreateGround' : GPU Shader couldn't compile");
}
shaderMaterial.uniforms.TextureBlendMap.value = _TextureBlendMap;
shaderMaterial.uniforms.TextureBackground.value = _TextureSand;
shaderMaterial.uniforms.TextureR.value = _TextureClay;
shaderMaterial.uniforms.TextureG.value = _TextureGrass;
shaderMaterial.uniforms.TextureB.value = _TextureSandRock;
var geometry = new THREE.BoxGeometry(GROUND_SIZE, GROUND_HEIGHT, GROUND_SIZE);
var mesh = new THREE.Mesh(geometry, shaderMaterial);
mesh.castShadow = false;
mesh.receiveShadow = true;
return mesh;
}
这是修改后的 PHONG 着色器:
BlendMapVertexShader = [
"#define PHONG",
"varying vec3 vViewPosition;",
"varying vec2 vUv;",
"#ifndef FLAT_SHADED",
"varying vec3 vNormal;",
"#endif",
THREE.ShaderChunk["common"],
THREE.ShaderChunk["uv_pars_vertex"],
THREE.ShaderChunk["uv2_pars_vertex"],
THREE.ShaderChunk["displacementmap_pars_vertex"],
THREE.ShaderChunk["envmap_pars_vertex"],
THREE.ShaderChunk["color_pars_vertex"],
THREE.ShaderChunk["morphtarget_pars_vertex"],
THREE.ShaderChunk["skinning_pars_vertex"],
THREE.ShaderChunk["shadowmap_pars_vertex"],
THREE.ShaderChunk["logdepthbuf_pars_vertex"],
THREE.ShaderChunk["clipping_planes_pars_vertex"],
"void main() {",
THREE.ShaderChunk["uv_vertex"],
THREE.ShaderChunk["uv2_vertex"],
THREE.ShaderChunk["color_vertex"],
THREE.ShaderChunk["beginnormal_vertex"],
THREE.ShaderChunk["morphnormal_vertex"],
THREE.ShaderChunk["skinbase_vertex"],
THREE.ShaderChunk["skinnormal_vertex"],
THREE.ShaderChunk["defaultnormal_vertex"],
"#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED",
"vNormal = normalize( transformedNormal );",
"#endif",
THREE.ShaderChunk["begin_vertex"],
THREE.ShaderChunk["displacementmap_vertex"],
THREE.ShaderChunk["morphtarget_vertex"],
THREE.ShaderChunk["skinning_vertex"],
THREE.ShaderChunk["project_vertex"],
THREE.ShaderChunk["logdepthbuf_vertex"],
THREE.ShaderChunk["clipping_planes_vertex"],
"vUv = uv;",
"vViewPosition = - mvPosition.xyz;",
THREE.ShaderChunk["worldpos_vertex"],
THREE.ShaderChunk["envmap_vertex"],
THREE.ShaderChunk["shadowmap_vertex"],
"}",
].join("\n");
BlendMapFragmentShader = [
"#define PHONG",
"varying vec2 vUv;",
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
"uniform float opacity;",
"uniform sampler2D TextureBlendMap;",
"uniform sampler2D TextureBackground;",
"uniform sampler2D TextureR;",
"uniform sampler2D TextureG;",
"uniform sampler2D TextureB;",
THREE.ShaderChunk["common"],
THREE.ShaderChunk["packing"],
THREE.ShaderChunk["color_pars_fragment"],
THREE.ShaderChunk["uv_pars_fragment"],
THREE.ShaderChunk["uv2_pars_fragment"],
THREE.ShaderChunk["map_pars_fragment"],
THREE.ShaderChunk["alphamap_pars_fragment"],
THREE.ShaderChunk["aomap_pars_fragment"],
THREE.ShaderChunk["lightmap_pars_fragment"],
THREE.ShaderChunk["emissivemap_pars_fragment"],
THREE.ShaderChunk["envmap_pars_fragment"],
THREE.ShaderChunk["fog_pars_fragment"],
THREE.ShaderChunk["bsdfs"],
THREE.ShaderChunk["lights_pars"],
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"],
THREE.ShaderChunk["logdepthbuf_pars_fragment"],
THREE.ShaderChunk["clipping_planes_pars_fragment"],
"void main() {",
THREE.ShaderChunk["clipping_planes_fragment"],
"// THIS IS CUSTOM CODE TO OVERRIDE THE DIFFUSE COLOR WITH BLENDMAP TEXTURES",
"vec4 cBlend = texture2D(TextureBlendMap, vUv);",
"float bText = 1.0 - (cBlend.r + cBlend.g + cBlend.b);",
"vec2 tiledCoords = vUv * 40.0;",
"vec4 cBack = texture2D(TextureBackground, tiledCoords) * bText;",
"vec4 cR = texture2D(TextureR, tiledCoords) * cBlend.r;",
"vec4 cG = texture2D(TextureG, tiledCoords) * cBlend.g;",
"vec4 cB = texture2D(TextureB, tiledCoords) * cBlend.b;",
"vec4 cTot = cBack + cR + cG + cB;",
"vec4 diffuseColor = vec4( diffuse, opacity );",
"diffuseColor.r = cTot.r;",
"diffuseColor.g = cTot.g;",
"diffuseColor.b = cTot.b;",
"ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
"vec3 totalEmissiveRadiance = emissive;",
THREE.ShaderChunk["logdepthbuf_fragment"],
THREE.ShaderChunk["map_fragment"],
THREE.ShaderChunk["color_fragment"],
THREE.ShaderChunk["alphamap_fragment"],
THREE.ShaderChunk["alphatest_fragment"],
THREE.ShaderChunk["specularmap_fragment"],
THREE.ShaderChunk["normal_flip"],
THREE.ShaderChunk["normal_fragment"],
THREE.ShaderChunk["emissivemap_fragment"],
"// accumulation",
THREE.ShaderChunk["lights_phong_fragment"],
THREE.ShaderChunk["lights_template"],
"// modulation",
THREE.ShaderChunk["aomap_fragment"],
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
THREE.ShaderChunk["envmap_fragment"],
"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
THREE.ShaderChunk["premultiplied_alpha_fragment"],
THREE.ShaderChunk["tonemapping_fragment"],
THREE.ShaderChunk["encodings_fragment"],
THREE.ShaderChunk["fog_fragment"],
"}",
].join("\n");
加上shaderMaterial.lights = true;
,我遇到了和你一样的问题。这是我自己写的完整打字稿class:threejs git issue
这是完整代码:
import * as THREE from 'three';
export class TerrainMaterial extends THREE.ShaderMaterial {
public constructor() {
super({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
THREE.UniformsLib.lights,
{
diffuse: { value: new THREE.Color(0xeeeeee) },
opacity: { value: 1.0 },
emissive: { value: new THREE.Color(0x000000) },
specular: { value: new THREE.Color(0x111111) },
shininess: { value: 0 },
offsetRepeat: { value: new THREE.Vector4(0, 0, 1, 1) },
map1: { value: null },
map2: { value: null },
map3: { value: null },
map1Normal: { value: null },
map2Normal: { value: null },
map3Normal: { value: null },
map1HeightRange: { value: 0 },
map2HeightRange: { value: 0 },
map3HeightRange: { value: 0 }
}
]),
vertexShader: [
"varying vec3 vNormal;",
"varying vec3 vViewPosition;",
"varying vec3 fPosition;",
"varying vec2 vUv;",
"uniform vec4 offsetRepeat;",
THREE.ShaderChunk.shadowmap_pars_vertex,
//THREE.ShaderChunk.logdepthbuf_pars_vertex,
THREE.ShaderChunk.fog_pars_vertex,
"void main(){",
THREE.ShaderChunk.beginnormal_vertex,
THREE.ShaderChunk.defaultnormal_vertex,
"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
"vNormal = normalize( transformedNormal );",
THREE.ShaderChunk.begin_vertex,
THREE.ShaderChunk.project_vertex,
//THREE.ShaderChunk.logdepthbuf_vertex,
"fPosition = position;",
"vViewPosition = - mvPosition.xyz;",
THREE.ShaderChunk.worldpos_vertex,
THREE.ShaderChunk.shadowmap_vertex,
THREE.ShaderChunk.fog_vertex,
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
"uniform float opacity;",
"uniform sampler2D map1;",
"uniform sampler2D map2;",
"uniform sampler2D map3;",
"uniform sampler2D map1Normal;",
"uniform sampler2D map2Normal;",
"uniform sampler2D map3Normal;",
"uniform float map1HeightRange;",
"uniform float map2HeightRange;",
"uniform float map3HeightRange;",
"varying vec2 vUv;",
"varying vec3 fPosition;",
THREE.ShaderChunk.common,
THREE.ShaderChunk.packing,
THREE.ShaderChunk.dithering_pars_fragment,
THREE.ShaderChunk.emissivemap_pars_fragment,
THREE.ShaderChunk.fog_pars_fragment,
THREE.ShaderChunk.bsdfs,
THREE.ShaderChunk.lights_pars,
THREE.ShaderChunk.lights_phong_pars_fragment,
THREE.ShaderChunk.shadowmap_pars_fragment,
THREE.ShaderChunk.specularmap_pars_fragment,
//THREE.ShaderChunk.logdepthbuf_pars_fragment,
"vec3 perturbNormal2Arb( vec3 normalColor, vec3 eye_pos, vec3 surf_norm ) {",
"vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );",
"vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );",
"vec2 st0 = dFdx( vUv.st );",
"vec2 st1 = dFdy( vUv.st );",
"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
"vec3 N = normalize( surf_norm );",
"vec3 mapN = normalColor * 2.0 - 1.0;",
//"mapN.xy = normalScale * mapN.xy;",
"mat3 tsn = mat3( S, T, N );",
"return normalize( tsn * mapN );",
"}",
"void main(){",
"vec4 diffuseColor = vec4( diffuse, opacity );",
"ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
"vec3 totalEmissiveRadiance = emissive;",
//THREE.ShaderChunk.logdepthbuf_fragment,
"vec3 texel;",
"vec3 texelNormal;",
"float amount;",
//need to optimize here, let's say remove if else if else?
"if(fPosition.y < map1HeightRange){",
"texel = texture2D(map1, vUv).rgb;",
"texelNormal = texture2D(map1Normal, vUv).rgb;",
"}",
"else if(fPosition.y < map2HeightRange){",
"amount = (fPosition.y - map1HeightRange) / map1HeightRange;",
"texel = mix(texture2D(map1, vUv), texture2D(map2, vUv), amount).rgb;",
"texelNormal = mix(texture2D(map1Normal, vUv), texture2D(map2Normal, vUv), amount).rgb;",
"}",
"else if(fPosition.y < map3HeightRange){",
"float hStep = map3HeightRange - map2HeightRange;",
"amount = (fPosition.y - hStep) / hStep;",
"texel = mix(texture2D(map2, vUv), texture2D(map3, vUv), amount).rgb;",
"texelNormal = mix(texture2D(map2Normal, vUv), texture2D(map3Normal, vUv), amount).rgb;",
"} else {",
"texel = texture2D(map3, vUv).rgb;",
"texelNormal = texture2D(map3Normal, vUv).rgb;",
"}",
"vec4 texelColor = vec4( texel, 1.0 );",
"texelColor = mapTexelToLinear( texelColor );",
"diffuseColor *= texelColor;",
THREE.ShaderChunk.specularmap_fragment,
"vec3 normal = normalize( vNormal );",
"normal = perturbNormal2Arb( texelNormal.rgb, -vViewPosition, normal );",
THREE.ShaderChunk.emissivemap_fragment,
THREE.ShaderChunk.lights_phong_fragment,
THREE.ShaderChunk.lights_template,
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
THREE.ShaderChunk.tonemapping_fragment,
THREE.ShaderChunk.encodings_fragment,
THREE.ShaderChunk.fog_fragment,
THREE.ShaderChunk.premultiplied_alpha_fragment,
THREE.ShaderChunk.dithering_fragment,
"}"
].join("\n")
});
this.defaultAttributeValues = null;
this.fog = true;
this.lights = true;
this.extensions.derivatives = true;
this.extensions.shaderTextureLOD = true;
}
public setOffsetRepeat(ofX: number, ofY: number, rpX: number, rpY: number):void {
this.uniforms["offsetRepeat"].value.set(ofX, ofY, rpX, rpY);
this.setRepeat(this.uniforms["map1"].value, this.uniforms["map1Normal"].value);
this.setRepeat(this.uniforms["map2"].value, this.uniforms["map2Normal"].value);
this.setRepeat(this.uniforms["map3"].value, this.uniforms["map3Normal"].value);
this.needsUpdate = true;
}
private setRepeat(map: THREE.Texture, normal: THREE.Texture): void {
let v4: THREE.Vector4 = this.uniforms["offsetRepeat"].value;
if (v4.y != 1 || v4.z != 1) {
if (map) map.wrapS = map.wrapT = THREE.RepeatWrapping;
if (normal) normal.wrapS = normal.wrapT = THREE.RepeatWrapping;
}
}
public setMap1(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map1"].value = map;
this.uniforms["map1Normal"].value = normal;
this.uniforms["map1HeightRange"].value = heightRange;
this.needsUpdate = true;
}
public setMap2(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map2"].value = map;
this.uniforms["map2Normal"].value = normal;
this.uniforms["map2HeightRange"].value = heightRange;
this.needsUpdate = true;
}
public setMap3(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map3"].value = map;
this.uniforms["map3Normal"].value = normal;
this.uniforms["map3HeightRange"].value = heightRange;
this.needsUpdate = true;
}
}
和用法:
let loader = new THREE.TextureLoader();
let terrainMat = new TerrainMaterial();
terrainMat.dithering = Config.DITHERING;
terrainMat.setOffsetRepeat(0, 0, 80, 80);
let map1 = loader.load("images/maps/ground/shatter.jpg");
let map1Normal = loader.load("images/maps/ground/shatter_normal.png");
map1.anisotropy = map1Normal.anisotropy = anisotropy;
let map2 = loader.load("images/maps/ground/earth.jpg");
let map2Normal = loader.load("images/maps/ground/earth_normal.png");
map2.anisotropy = map2Normal.anisotropy = anisotropy;
let map3 = loader.load("images/maps/ground/moss.jpg");
let map3Normal = loader.load("images/maps/ground/moss_normal.png");
map3.anisotropy = map3Normal.anisotropy = anisotropy;
let hStep = GroundGeometry.MAX_HEIGHT / 4;
terrainMat.setMap1(map1, map1Normal, hStep);
terrainMat.setMap2(map2, map2Normal, hStep * 2);
terrainMat.setMap3(map3, map3Normal, hStep * 4);
//note: replace new THREE.PlaneGeometry with your terrain geometry so the mateiral can detect height change. otherise the plane will be filled full with the same 1 map.
let p = new THREE.Mesh(new THREE.PlaneGeometry(900, 900, 257, 257), terrainMat);
p.geometry.rotateX(-NumberConsts.PI_2);
scene.add(p);
结果:
click to see image (i don't have the privilege to embed images yet)
我创建了一个自定义着色器,以便能够使用具有 4 种不同纹理的 BlendMap,但我无法使用它来获得 shadows/lightning 效果。
我在这里错过了什么?或者有其他方法可以实现相同的功能吗?
下面的网格创建方法显示了正确混合的所有纹理。
// Creates the ground
function CreateGround() {
var uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib["lights"],
THREE.UniformsLib["shadowmap"],
{
TextureBackground: { type: "t", value: null },
TextureR: { type: "t", value: null },
TextureG: { type: "t", value: null },
TextureB: { type: "t", value: null },
TextureBlendMap: { type: "t", value: null }
}]);
var shaderMaterial;
try {
shaderMaterial = new THREE.ShaderMaterial({
lights: true,
uniforms: uniforms,
vertexShader: BlendMapVertexShader,
fragmentShader: BlendMapFragmentShader
});
} catch (e) {
alert("Error 'CreateGround' : GPU Shader couldn't compile");
}
shaderMaterial.uniforms.TextureBlendMap.value = _TextureBlendMap;
shaderMaterial.uniforms.TextureBackground.value = _TextureSand;
shaderMaterial.uniforms.TextureR.value = _TextureClay;
shaderMaterial.uniforms.TextureG.value = _TextureClay;
shaderMaterial.uniforms.TextureB.value = _TextureRock;
var geometry = new THREE.BoxGeometry(GROUND_SIZE, GROUND_HEIGHT, GROUND_SIZE);
var mesh = new THREE.Mesh(geometry, shaderMaterial);
mesh.castShadow = false;
mesh.receiveShadow = true;
return mesh;
}
这是我当前的着色器:
BlendMapVertexShader = [
THREE.ShaderChunk["shadowmap_pars_vertex"],
"varying vec2 vUv;",
"varying vec3 vPosition;",
"void main( void ) {",
"vUv = uv;",
"vPosition = position;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);",
THREE.ShaderChunk["begin_vertex"],
THREE.ShaderChunk["worldpos_vertex"],
THREE.ShaderChunk["shadowmap_vertex"],
"}",
].join("\n");
BlendMapFragmentShader = [
THREE.ShaderChunk["common"],
THREE.ShaderChunk["packing"],
THREE.ShaderChunk["shadowmap_pars_fragment"],
"varying vec2 vUv;",
"varying vec3 vPosition;",
"uniform sampler2D TextureBlendMap;",
"uniform sampler2D TextureBackground;",
"uniform sampler2D TextureR;",
"uniform sampler2D TextureG;",
"uniform sampler2D TextureB;",
"void main() {",
"vec4 cBlend = texture2D(TextureBlendMap, vUv);",
"float bText = 1.0 - (cBlend.r + cBlend.g + cBlend.b);",
"vec2 tiledCoords = vUv * 40.0;",
"vec4 cBack = texture2D(TextureBackground, tiledCoords) * bText;",
"vec4 cR = texture2D(TextureR, tiledCoords) * cBlend.r;",
"vec4 cG = texture2D(TextureG, tiledCoords) * cBlend.g;",
"vec4 cB = texture2D(TextureB, tiledCoords) * cBlend.b;",
"vec4 cTot = cBack + cR + cG + cB;",
"gl_FragColor = cTot;",
THREE.ShaderChunk["shadowmap_fragment"],
"}",
].join("\n");
我在浏览器中没有错误或警告。
three.js r85Dev
这是一个长镜头,但如果场景中的所有对象都使用此 material,则不应有任何阴影,因为没有任何东西会投射阴影。
mesh.castShadow = false;
mesh.receiveShadow = true;
但我猜测在您的其余代码中,您将一些网格设置为投射阴影。
我不知道我是否完全理解 ShaderChunk 方法,但它看起来只是一系列定义的字符串,您将其放入自己的文本中间,这部分看起来有点可疑:
"gl_FragColor = cTot;",
THREE.ShaderChunk["shadowmap_fragment"],
但我看不出 shadowmap_fragment 所做的任何事情如何有机会与我们现有的 gl_FragColor 值混合。在 shadowmap_fragment src 它运行:
void main() {
gl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );
}
其中 getShadowMask() 对 return 浮动的灯光做了很多魔术。所以我看不出您分配的 gl_FragColor 是如何与 shadowmap_fragment return 混合在一起的。换个顺序怎么样:
THREE.ShaderChunk["shadowmap_fragment"],
"gl_FragColor = cTot*gl_FragColor.a;"
不确定这是否有帮助,但您可以查看 shadowmap_pars_frag 的 src 以了解幕后发生的事情。也许我们遗漏了什么。
最后一件事:尝试只使用 none 纹理混合的阴影来隔离问题。可能是您达到了纹理采样数量的 GLSL 限制 - 因为阴影块也使用纹理采样。
好的。我最终复制了 PHONG 着色器,然后覆盖了漫反射颜色输入。解决方案如下。
这是用blendmapshader创建网格的方法
// Creates the ground
function CreateGround(material) {
var uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib["common"],
THREE.UniformsLib["aomap"],
THREE.UniformsLib["lightmap"],
THREE.UniformsLib["emissivemap"],
THREE.UniformsLib["bumpmap"],
THREE.UniformsLib["normalmap"],
THREE.UniformsLib["displacementmap"],
THREE.UniformsLib["gradientmap"],
THREE.UniformsLib["fog"],
THREE.UniformsLib["lights"],
{
emissive: { type: "c", value: new THREE.Color(0x000000) },
specular: { type: "c", value: new THREE.Color(0x111111) },
shininess: { type: "f", value: 30 },
TextureBackground: { type: "t", value: null },
TextureR: { type: "t", value: null },
TextureG: { type: "t", value: null },
TextureB: { type: "t", value: null },
TextureBlendMap: { type: "t", value: null },
}]);
var shaderMaterial;
try {
shaderMaterial = new THREE.ShaderMaterial({
lights: true,
uniforms: uniforms,
vertexShader: BlendMapVertexShader,
fragmentShader: BlendMapFragmentShader
});
} catch (e) {
alert("Error 'CreateGround' : GPU Shader couldn't compile");
}
shaderMaterial.uniforms.TextureBlendMap.value = _TextureBlendMap;
shaderMaterial.uniforms.TextureBackground.value = _TextureSand;
shaderMaterial.uniforms.TextureR.value = _TextureClay;
shaderMaterial.uniforms.TextureG.value = _TextureGrass;
shaderMaterial.uniforms.TextureB.value = _TextureSandRock;
var geometry = new THREE.BoxGeometry(GROUND_SIZE, GROUND_HEIGHT, GROUND_SIZE);
var mesh = new THREE.Mesh(geometry, shaderMaterial);
mesh.castShadow = false;
mesh.receiveShadow = true;
return mesh;
}
这是修改后的 PHONG 着色器:
BlendMapVertexShader = [
"#define PHONG",
"varying vec3 vViewPosition;",
"varying vec2 vUv;",
"#ifndef FLAT_SHADED",
"varying vec3 vNormal;",
"#endif",
THREE.ShaderChunk["common"],
THREE.ShaderChunk["uv_pars_vertex"],
THREE.ShaderChunk["uv2_pars_vertex"],
THREE.ShaderChunk["displacementmap_pars_vertex"],
THREE.ShaderChunk["envmap_pars_vertex"],
THREE.ShaderChunk["color_pars_vertex"],
THREE.ShaderChunk["morphtarget_pars_vertex"],
THREE.ShaderChunk["skinning_pars_vertex"],
THREE.ShaderChunk["shadowmap_pars_vertex"],
THREE.ShaderChunk["logdepthbuf_pars_vertex"],
THREE.ShaderChunk["clipping_planes_pars_vertex"],
"void main() {",
THREE.ShaderChunk["uv_vertex"],
THREE.ShaderChunk["uv2_vertex"],
THREE.ShaderChunk["color_vertex"],
THREE.ShaderChunk["beginnormal_vertex"],
THREE.ShaderChunk["morphnormal_vertex"],
THREE.ShaderChunk["skinbase_vertex"],
THREE.ShaderChunk["skinnormal_vertex"],
THREE.ShaderChunk["defaultnormal_vertex"],
"#ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED",
"vNormal = normalize( transformedNormal );",
"#endif",
THREE.ShaderChunk["begin_vertex"],
THREE.ShaderChunk["displacementmap_vertex"],
THREE.ShaderChunk["morphtarget_vertex"],
THREE.ShaderChunk["skinning_vertex"],
THREE.ShaderChunk["project_vertex"],
THREE.ShaderChunk["logdepthbuf_vertex"],
THREE.ShaderChunk["clipping_planes_vertex"],
"vUv = uv;",
"vViewPosition = - mvPosition.xyz;",
THREE.ShaderChunk["worldpos_vertex"],
THREE.ShaderChunk["envmap_vertex"],
THREE.ShaderChunk["shadowmap_vertex"],
"}",
].join("\n");
BlendMapFragmentShader = [
"#define PHONG",
"varying vec2 vUv;",
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
"uniform float opacity;",
"uniform sampler2D TextureBlendMap;",
"uniform sampler2D TextureBackground;",
"uniform sampler2D TextureR;",
"uniform sampler2D TextureG;",
"uniform sampler2D TextureB;",
THREE.ShaderChunk["common"],
THREE.ShaderChunk["packing"],
THREE.ShaderChunk["color_pars_fragment"],
THREE.ShaderChunk["uv_pars_fragment"],
THREE.ShaderChunk["uv2_pars_fragment"],
THREE.ShaderChunk["map_pars_fragment"],
THREE.ShaderChunk["alphamap_pars_fragment"],
THREE.ShaderChunk["aomap_pars_fragment"],
THREE.ShaderChunk["lightmap_pars_fragment"],
THREE.ShaderChunk["emissivemap_pars_fragment"],
THREE.ShaderChunk["envmap_pars_fragment"],
THREE.ShaderChunk["fog_pars_fragment"],
THREE.ShaderChunk["bsdfs"],
THREE.ShaderChunk["lights_pars"],
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"],
THREE.ShaderChunk["logdepthbuf_pars_fragment"],
THREE.ShaderChunk["clipping_planes_pars_fragment"],
"void main() {",
THREE.ShaderChunk["clipping_planes_fragment"],
"// THIS IS CUSTOM CODE TO OVERRIDE THE DIFFUSE COLOR WITH BLENDMAP TEXTURES",
"vec4 cBlend = texture2D(TextureBlendMap, vUv);",
"float bText = 1.0 - (cBlend.r + cBlend.g + cBlend.b);",
"vec2 tiledCoords = vUv * 40.0;",
"vec4 cBack = texture2D(TextureBackground, tiledCoords) * bText;",
"vec4 cR = texture2D(TextureR, tiledCoords) * cBlend.r;",
"vec4 cG = texture2D(TextureG, tiledCoords) * cBlend.g;",
"vec4 cB = texture2D(TextureB, tiledCoords) * cBlend.b;",
"vec4 cTot = cBack + cR + cG + cB;",
"vec4 diffuseColor = vec4( diffuse, opacity );",
"diffuseColor.r = cTot.r;",
"diffuseColor.g = cTot.g;",
"diffuseColor.b = cTot.b;",
"ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
"vec3 totalEmissiveRadiance = emissive;",
THREE.ShaderChunk["logdepthbuf_fragment"],
THREE.ShaderChunk["map_fragment"],
THREE.ShaderChunk["color_fragment"],
THREE.ShaderChunk["alphamap_fragment"],
THREE.ShaderChunk["alphatest_fragment"],
THREE.ShaderChunk["specularmap_fragment"],
THREE.ShaderChunk["normal_flip"],
THREE.ShaderChunk["normal_fragment"],
THREE.ShaderChunk["emissivemap_fragment"],
"// accumulation",
THREE.ShaderChunk["lights_phong_fragment"],
THREE.ShaderChunk["lights_template"],
"// modulation",
THREE.ShaderChunk["aomap_fragment"],
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
THREE.ShaderChunk["envmap_fragment"],
"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
THREE.ShaderChunk["premultiplied_alpha_fragment"],
THREE.ShaderChunk["tonemapping_fragment"],
THREE.ShaderChunk["encodings_fragment"],
THREE.ShaderChunk["fog_fragment"],
"}",
].join("\n");
加上shaderMaterial.lights = true;
,我遇到了和你一样的问题。这是我自己写的完整打字稿class:threejs git issue
这是完整代码:
import * as THREE from 'three';
export class TerrainMaterial extends THREE.ShaderMaterial {
public constructor() {
super({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
THREE.UniformsLib.lights,
{
diffuse: { value: new THREE.Color(0xeeeeee) },
opacity: { value: 1.0 },
emissive: { value: new THREE.Color(0x000000) },
specular: { value: new THREE.Color(0x111111) },
shininess: { value: 0 },
offsetRepeat: { value: new THREE.Vector4(0, 0, 1, 1) },
map1: { value: null },
map2: { value: null },
map3: { value: null },
map1Normal: { value: null },
map2Normal: { value: null },
map3Normal: { value: null },
map1HeightRange: { value: 0 },
map2HeightRange: { value: 0 },
map3HeightRange: { value: 0 }
}
]),
vertexShader: [
"varying vec3 vNormal;",
"varying vec3 vViewPosition;",
"varying vec3 fPosition;",
"varying vec2 vUv;",
"uniform vec4 offsetRepeat;",
THREE.ShaderChunk.shadowmap_pars_vertex,
//THREE.ShaderChunk.logdepthbuf_pars_vertex,
THREE.ShaderChunk.fog_pars_vertex,
"void main(){",
THREE.ShaderChunk.beginnormal_vertex,
THREE.ShaderChunk.defaultnormal_vertex,
"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
"vNormal = normalize( transformedNormal );",
THREE.ShaderChunk.begin_vertex,
THREE.ShaderChunk.project_vertex,
//THREE.ShaderChunk.logdepthbuf_vertex,
"fPosition = position;",
"vViewPosition = - mvPosition.xyz;",
THREE.ShaderChunk.worldpos_vertex,
THREE.ShaderChunk.shadowmap_vertex,
THREE.ShaderChunk.fog_vertex,
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 diffuse;",
"uniform vec3 emissive;",
"uniform vec3 specular;",
"uniform float shininess;",
"uniform float opacity;",
"uniform sampler2D map1;",
"uniform sampler2D map2;",
"uniform sampler2D map3;",
"uniform sampler2D map1Normal;",
"uniform sampler2D map2Normal;",
"uniform sampler2D map3Normal;",
"uniform float map1HeightRange;",
"uniform float map2HeightRange;",
"uniform float map3HeightRange;",
"varying vec2 vUv;",
"varying vec3 fPosition;",
THREE.ShaderChunk.common,
THREE.ShaderChunk.packing,
THREE.ShaderChunk.dithering_pars_fragment,
THREE.ShaderChunk.emissivemap_pars_fragment,
THREE.ShaderChunk.fog_pars_fragment,
THREE.ShaderChunk.bsdfs,
THREE.ShaderChunk.lights_pars,
THREE.ShaderChunk.lights_phong_pars_fragment,
THREE.ShaderChunk.shadowmap_pars_fragment,
THREE.ShaderChunk.specularmap_pars_fragment,
//THREE.ShaderChunk.logdepthbuf_pars_fragment,
"vec3 perturbNormal2Arb( vec3 normalColor, vec3 eye_pos, vec3 surf_norm ) {",
"vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );",
"vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );",
"vec2 st0 = dFdx( vUv.st );",
"vec2 st1 = dFdy( vUv.st );",
"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
"vec3 N = normalize( surf_norm );",
"vec3 mapN = normalColor * 2.0 - 1.0;",
//"mapN.xy = normalScale * mapN.xy;",
"mat3 tsn = mat3( S, T, N );",
"return normalize( tsn * mapN );",
"}",
"void main(){",
"vec4 diffuseColor = vec4( diffuse, opacity );",
"ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
"vec3 totalEmissiveRadiance = emissive;",
//THREE.ShaderChunk.logdepthbuf_fragment,
"vec3 texel;",
"vec3 texelNormal;",
"float amount;",
//need to optimize here, let's say remove if else if else?
"if(fPosition.y < map1HeightRange){",
"texel = texture2D(map1, vUv).rgb;",
"texelNormal = texture2D(map1Normal, vUv).rgb;",
"}",
"else if(fPosition.y < map2HeightRange){",
"amount = (fPosition.y - map1HeightRange) / map1HeightRange;",
"texel = mix(texture2D(map1, vUv), texture2D(map2, vUv), amount).rgb;",
"texelNormal = mix(texture2D(map1Normal, vUv), texture2D(map2Normal, vUv), amount).rgb;",
"}",
"else if(fPosition.y < map3HeightRange){",
"float hStep = map3HeightRange - map2HeightRange;",
"amount = (fPosition.y - hStep) / hStep;",
"texel = mix(texture2D(map2, vUv), texture2D(map3, vUv), amount).rgb;",
"texelNormal = mix(texture2D(map2Normal, vUv), texture2D(map3Normal, vUv), amount).rgb;",
"} else {",
"texel = texture2D(map3, vUv).rgb;",
"texelNormal = texture2D(map3Normal, vUv).rgb;",
"}",
"vec4 texelColor = vec4( texel, 1.0 );",
"texelColor = mapTexelToLinear( texelColor );",
"diffuseColor *= texelColor;",
THREE.ShaderChunk.specularmap_fragment,
"vec3 normal = normalize( vNormal );",
"normal = perturbNormal2Arb( texelNormal.rgb, -vViewPosition, normal );",
THREE.ShaderChunk.emissivemap_fragment,
THREE.ShaderChunk.lights_phong_fragment,
THREE.ShaderChunk.lights_template,
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;",
"gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
THREE.ShaderChunk.tonemapping_fragment,
THREE.ShaderChunk.encodings_fragment,
THREE.ShaderChunk.fog_fragment,
THREE.ShaderChunk.premultiplied_alpha_fragment,
THREE.ShaderChunk.dithering_fragment,
"}"
].join("\n")
});
this.defaultAttributeValues = null;
this.fog = true;
this.lights = true;
this.extensions.derivatives = true;
this.extensions.shaderTextureLOD = true;
}
public setOffsetRepeat(ofX: number, ofY: number, rpX: number, rpY: number):void {
this.uniforms["offsetRepeat"].value.set(ofX, ofY, rpX, rpY);
this.setRepeat(this.uniforms["map1"].value, this.uniforms["map1Normal"].value);
this.setRepeat(this.uniforms["map2"].value, this.uniforms["map2Normal"].value);
this.setRepeat(this.uniforms["map3"].value, this.uniforms["map3Normal"].value);
this.needsUpdate = true;
}
private setRepeat(map: THREE.Texture, normal: THREE.Texture): void {
let v4: THREE.Vector4 = this.uniforms["offsetRepeat"].value;
if (v4.y != 1 || v4.z != 1) {
if (map) map.wrapS = map.wrapT = THREE.RepeatWrapping;
if (normal) normal.wrapS = normal.wrapT = THREE.RepeatWrapping;
}
}
public setMap1(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map1"].value = map;
this.uniforms["map1Normal"].value = normal;
this.uniforms["map1HeightRange"].value = heightRange;
this.needsUpdate = true;
}
public setMap2(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map2"].value = map;
this.uniforms["map2Normal"].value = normal;
this.uniforms["map2HeightRange"].value = heightRange;
this.needsUpdate = true;
}
public setMap3(map: THREE.Texture, normal: THREE.Texture, heightRange: number) {
this.setRepeat(map, normal);
this.uniforms["map3"].value = map;
this.uniforms["map3Normal"].value = normal;
this.uniforms["map3HeightRange"].value = heightRange;
this.needsUpdate = true;
}
}
和用法:
let loader = new THREE.TextureLoader();
let terrainMat = new TerrainMaterial();
terrainMat.dithering = Config.DITHERING;
terrainMat.setOffsetRepeat(0, 0, 80, 80);
let map1 = loader.load("images/maps/ground/shatter.jpg");
let map1Normal = loader.load("images/maps/ground/shatter_normal.png");
map1.anisotropy = map1Normal.anisotropy = anisotropy;
let map2 = loader.load("images/maps/ground/earth.jpg");
let map2Normal = loader.load("images/maps/ground/earth_normal.png");
map2.anisotropy = map2Normal.anisotropy = anisotropy;
let map3 = loader.load("images/maps/ground/moss.jpg");
let map3Normal = loader.load("images/maps/ground/moss_normal.png");
map3.anisotropy = map3Normal.anisotropy = anisotropy;
let hStep = GroundGeometry.MAX_HEIGHT / 4;
terrainMat.setMap1(map1, map1Normal, hStep);
terrainMat.setMap2(map2, map2Normal, hStep * 2);
terrainMat.setMap3(map3, map3Normal, hStep * 4);
//note: replace new THREE.PlaneGeometry with your terrain geometry so the mateiral can detect height change. otherise the plane will be filled full with the same 1 map.
let p = new THREE.Mesh(new THREE.PlaneGeometry(900, 900, 257, 257), terrainMat);
p.geometry.rotateX(-NumberConsts.PI_2);
scene.add(p);
结果: click to see image (i don't have the privilege to embed images yet)