使用 ShaderMaterial 复制 MeshLambertMaterial 会忽略纹理

Posted

技术标签:

【中文标题】使用 ShaderMaterial 复制 MeshLambertMaterial 会忽略纹理【英文标题】:Replicating MeshLambertMaterial Using ShaderMaterial ignores textures 【发布时间】:2014-03-22 14:21:56 【问题描述】:

我注意到 THREE.js 在内部使用着色器来创建核心材质“例如 MeshLambertMaterial”,因此我决定将 Lambert 着色器从 Three.js 代码复制到一个新的着色器中并在其上构建。

这是我得到的代码(忠实地从 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;

现在纹理已加载并显示,但看起来纹理坐标被弄乱了,而不是重复,着色器似乎忽略了我提供的重复值。

为什么我需要那个 hack 来处理我的纹理,我可以做些什么来获得正确的纹理重复?

【问题讨论】:

three.js 被设计为易于使用,不易修改。这可能会在未来发生变化......首先,尝试添加material.defines,如下所示:var defines = ; defines[ "USE_MAP" ] = "";。在材质构造函数中指定defines: defines,看看是否有帮助。 谢谢,您的修改与我使用的“materials.map”破解效果相同。纹理贴图已加载,但没有重复 shaderUniforms[ "offsetRepeat" ].value.set( 0, 0, 2, 2 ); 解决了问题,但我似乎无法将其标记为已解决 我为你创建了一个答案。 【参考方案1】:

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 是的。 ---

以上是关于使用 ShaderMaterial 复制 MeshLambertMaterial 会忽略纹理的主要内容,如果未能解决你的问题,请参考以下文章

绘制不断变色的台体,BufferGeometry和ShaderMaterial的黄金组合(three.js实战5)

绘制彩色立方体,使用attribute变量向着色器传值,BufferGeometry和ShaderMaterial配合使用(three.js实战6)

SphereBufferGeometry 上的 THREE.js ShaderMaterial UV 包裹问题

Godot Shader笔记:着色器材质ShaderMaterial

网格Three.js上两种材质之间的动画

如何在 Three.js 中用 ShaderMaterial 替换 Collada 导入的纹理?