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 7

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");