ThreeJS [r85]: пользовательский шейдер с Shadowmap
Я создал пользовательский шейдер, чтобы иметь возможность использовать BlendMap с 4 различными текстурами, но я не могу заставить тени/эффекты молнии работать с ним.
Чего мне здесь не хватает? Или есть какой-то другой способ добиться такой же функциональности?
Метод создания сетки, ниже которого показаны все текстуры, смешанные должным образом.
// 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");
У меня нет ошибок или предупреждений в браузере.
Три.js r85Dev
3 ответа:
Просто добавьте
shaderMaterial.lights = true;
, я столкнулся с той же проблемой, что и вы. вот полный класс машинописи, написанный мной: 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);
И результат: нажмите, чтобы увидеть изображение (я еще не имею права вставлять изображения)
Это своего рода длинный кадр, но если все объекты в вашей сцене используют этот материал, то не должно быть никаких теней, потому что ничто не отбрасывает тени.
Но я предполагаю, что в остальной части вашего кода у вас есть некоторые сетки, настроенные для отбрасывания теней.mesh.castShadow = false; mesh.receiveShadow = true;
Я не знаю, полностью ли я понимаю методы Шейдерчанка, но похоже, что это просто ряд определенных строк, которые вы бросаете в середину собственного текста, эта часть выглядела немного подозрительно:
"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 () делает много магии с огнями, чтобы вернуть поплавок. Поэтому я не вижу, как ваш назначенный gl_FragColor смешивается с тем, что возвращает shadowmap_fragment. Как насчет изменения порядка:
THREE.ShaderChunk["shadowmap_fragment"], "gl_FragColor = cTot*gl_FragColor.a;"
Не уверен, что это поможет, но вы можете проверить src из shadowmap_pars_frag , чтобы посмотрите, что происходит за кулисами. Может быть, мы что-то упустили.
И последнее: попробуйте делать только тени без смешивания текстур, чтобы изолировать проблему. Возможно, вы задаете ограничение GLSL на количество выборок текстур - потому что теневой фрагмент также использует выборку текстур.
Хорошо. В итоге я скопировал Фонг-шейдер, а затем переопределил вход диффузного цвета. Решение ниже.
Это метод для создания сетки с 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; }
А это модифицированный Фонг-шейдер:
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");