复制MeshLambertMaterial使用ShaderMaterial忽略纹理

9
我注意到THREE.js内部使用着色器创建核心材质,如MeshLambertMaterial。因此,我决定从Three.js代码中复制lambert着色器到一个新的着色器,并在此基础上进行修改。
以下是我得到的代码(完全从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")

}

这是我用来设置统一的代码并创建材质的。

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

问题在于当我使用这些参数创建MeshLambertMaterial时,可以获得正确的光照和纹理重复效果,但是当我使用它来创建ShaderMaterial时,灯光和阴影似乎工作正常,但纹理贴图没有加载。为了解决这个问题,我研究了代码并设法通过在材质定义之后添加这个丑陋的“hack”来加载地图。
material.map = true;

现在纹理已经被加载和显示了,但看起来纹理坐标有些混乱。着色器似乎忽略了我提供的重复值,而不是重复。为什么我需要那个技巧来处理我的纹理,我该如何获得正确的纹理重复?

3
three.js旨在易于使用,而不是易于修改。这种情况可能会在未来发生变化...首先,尝试像这样添加'material.defines':var defines = {}; defines["USE_MAP"] = "";defines: defines指定在材质构造函数中,看看是否有所帮助。 - WestLangley
谢谢,您的修改产生了与我使用的“materials.map”黑客相同的效果。纹理贴图已加载,但重复次数不正确。 - Mostafa Abdelraouf
1
shaderUniforms[ "offsetRepeat" ].value.set( 0, 0, 2, 2 ); - WestLangley
那解决了问题,但我似乎无法将其标记为已解决。 - Mostafa Abdelraouf
我为你创建了一个答案。 - WestLangley
如果有人在关注这个问题,material.map = true; 是错误的设置。将地图设置为纹理。https://discourse.threejs.org/t/solved-how-do-we-use-map-uniform-with-custom-shadermaterial/1919 - Hobbes
1个回答

10

three.js旨在易于使用,但不易于修改。这可能会在未来发生改变...

您需要像以下这样设置material.defines

var defines = {};

defines[ "USE_MAP" ] = "";. 

然后在材料构造函数中指定defines
var material =  new THREE.ShaderMaterial({
   name: "TerrainShader",
   defines     : defines,
   uniforms    : shaderUniforms,
   vertexShader: THREE.MyShader.vertexShader,
   fragmentShader: THREE.MyShader.fragmentShader,
   fog:false,
   lights:true
});

关于纹理重复,您需要将重复功能添加到您的统一变量中:

shaderUniforms[ "offsetRepeat" ].value.set( 0, 0, 2, 2 );

three.js r.66


这仍然是向着着色器材质添加阴影支持的首选方法吗(r92)? - Hobbes
1
@Hobbes 是的。 - WestLangley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接