three.js 中的多贴图通道模型和纹理
Multi-map-channel models and textures in three.js
我正在尝试在 three.js 中使用两个以上的贴图通道来为我的模型分配纹理。令我惊讶的是,three.js 中的原生 materials 不支持多组 uvs。因此我必须使用 ShaderMaterial 并编写自己的 vertex/fragment 着色器才能访问多个贴图通道。
现在我确实设法让多通道 uv 与我写的一个简单代码一起工作:
<script id="vertex_shh" type="x-shader/x-vertex">
//VERTEX SHADER//
varying vec2 vUv;
attribute vec2 uv2;
varying vec2 vUv2;
attribute vec2 uv3;
varying vec2 vUv3;
attribute vec2 uv4;
varying vec2 vUv4;
attribute vec2 uv5;
varying vec2 vUv5;
void main()
{
vUv = uv;
vUv2 = uv2;
vUv3 = uv3;
vUv4 = uv4;
vUv5 = uv5;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
// END //
</script>
<script id="fragment_shh" type="x-shader/x-fragment">
//FRAGMENT SHADER//
uniform sampler2D tCh1;
uniform sampler2D tCh2;
uniform sampler2D tCh3;
uniform sampler2D tCh4;
uniform sampler2D tCh5;
varying vec2 vUv;
varying vec2 vUv2;
varying vec2 vUv3;
varying vec2 vUv4;
varying vec2 vUv5;
void main(void)
{
vec3 DiffuseComp;
vec4 TexCh1 = texture2D(tCh1, vUv);
vec4 TexCh2 = texture2D(tCh2, vUv2);
vec4 TexCh3 = texture2D(tCh3, vUv3);
vec4 TexCh4 = texture2D(tCh4, vUv4);
vec4 TexCh5 = texture2D(tCh5, vUv5);
DiffuseComp = ((TexCh1.rgb * (1.0 - TexCh4.rgb) + (TexCh2.rgb * TexCh4.rgb)) * (1.0 - TexCh5.rgb)) + (TexCh3.rgb * TexCh5.rgb);
gl_FragColor= vec4(DiffuseComp, 1.0);
}
// END //
/* SHADER */
var vertShader = document.getElementById('vertex_shh').innerHTML;
var fragShader = document.getElementById('fragment_shh').innerHTML;
var attributes = {};
var uniforms = {
tCh1: { type: "t", value: Textura1},
tCh2: { type: "t", value: Textura2},
tCh3: { type: "t", value: Textura3},
tCh4: { type: "t", value: Textura4},
tCh5: { type: "t", value: Textura5}
};
/* MATERIAL */
var multitexsh = new THREE.ShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: vertShader,
fragmentShader: fragShader
});
但是,我的问题是我基本上需要一个具有此功能的着色器和 Phong 着色器的一些基本功能:照明(来自场景灯)和镜面高光。我完全迷失在这里。我已经尝试寻找将此功能添加到本机着色器或将提到的功能从 phong material 添加到我自己的着色器的方法,但没有发现任何信息足以用我非常基本的编码技能来做到这一点。 .有人可以介入并帮助我吗?
编辑:
感谢 pailhead 让它与 onBeforeCompile 一起工作。这是在 r89 上,更新后的代码如下:
/* MANAGERS, LOADERS */
var manager = new THREE.LoadingManager();
var loader = new THREE.FBXLoader( manager );
var textureLoader = new THREE.TextureLoader( manager );
/*TEXTURE */
var prodtex ='textures/testing/cubetex.jpg';
var prodtex2 ='textures/testing/cube_blendmap.jpg';
var Textura1 = textureLoader.load (prodtex);
var Textura2 = textureLoader.load (prodtex);
var Textura3 = textureLoader.load (prodtex2);
/* MATERIAL */
var MAT = new THREE.MeshPhongMaterial();
MAT.map = Textura1;
/* MY SHADER CHANGES*/
const uv2_chunkToReplaceVS = 'attribute vec2 uv2; varying vec2 vUv2; attribute vec2 uv3; varying vec2 vUv3;'
const uv2_chunkToReplaceVSV = 'vUv2 = uv2; vUv3 = uv3;'
const uv2_chunkToReplaceFS = 'uniform sampler2D tCh1; uniform sampler2D tCh2; uniform sampler2D tCh3; varying vec2 vUv2; varying vec2 vUv3;'
const map_chunkToReplaceFSV = 'vec4 TexCh1 = texture2D(tCh1, vUv); vec4 TexCh2 = texture2D(tCh2, vUv2); vec4 TexCh3 = texture2D(tCh3, vUv3); vec4 texelColor = texture2D( map, vUv ); texelColor = mapTexelToLinear( texelColor ); TexCh1 = mapTexelToLinear( TexCh1 ); TexCh2 = mapTexelToLinear( TexCh2 ); TexCh2 = mapTexelToLinear( TexCh2 ); diffuseColor *= TexCh1 * (1.0 - TexCh3) + (TexCh2 * TexCh3);'
MAT.onBeforeCompile = shader=>{
shader.uniforms.tCh1= {type: "t", value: Textura1};
shader.uniforms.tCh2= {type: "t", value: Textura2};
shader.uniforms.tCh3= {type: "t", value: Textura3};
shader.fragmentShader = shader.fragmentShader.replace('#include <map_fragment>', map_chunkToReplaceFSV);
shader.vertexShader = shader.vertexShader.replace('#include <uv2_pars_vertex>', uv2_chunkToReplaceVS );
shader.vertexShader = shader.vertexShader.replace('#include <uv2_vertex>', uv2_chunkToReplaceVSV );
shader.fragmentShader = shader.fragmentShader.replace('#include <uv2_pars_fragment>', uv2_chunkToReplaceFS);
};
/* MODEL */
loader.load( ('models/' + 'cube' + '.FBX'), function( object ) {
//set material//
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = MAT;
};
} );
scene.add (object);
});
您可以使用 material 的一个未记录的功能,称为 onBeforeCompile
。
这是在三个解析着色器模板之前和编译着色器之前调用的回调。
模板可能如下所示
//someTemplate.vert
#include <common>
#include <some_pars_vertex>
void main(){
#include <some_vertex>
#include <another_vertex>
gl_Position = projectionMatrix * foobar;
}
假设您要添加更多 uv 通道。你会在 GLSL 中做一些 snippets/functions。这必须发生在全局范围内(void main(){...}
以上):
//myBeforeVoidMain.vert
attribute vec2 aUV2;
attribute vec2 aUV3;
varying vec2 vUv2;
varying vec2 vUv3;
然后这必须发生在主函数中:
//myUv.vert
vUv2 = aUV2;
vUv3 = aUV3;
您可以注入此代码,但在何处以及如何注入?
如何:
const chunkToReplace = THREE.ShaderChunk.uv_pars_vertex
myMaterial = new THREE.MeshPhongMaterial()
myMaterial.onBeforeCompile( shader=>{
shader.uniforms.myUniform = {value:foo}
shader.vertexShader = shader.vertexShader.replace( '#include <uv_pars_vertex>', chunkToReplace + myChunk )
})
其中:
您需要熟悉两者:
https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk
https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderLib
你需要考虑一下。可以注意到 <common>
存在于几乎每个库的全局范围内。但是,因为它包含在垂直着色器和碎片着色器中,所以您不能在其中提及 attribute
,碎片着色器将无法编译。
向顶点着色器添加属性、制服和变量的一个很好的块是uv_pars_vertex
,因为它也存在于几乎所有着色器中。
对于这个特殊问题,您还必须扩展片段着色器。同样,varyings 将被放置在 uv_pars_fragment
中,然后您必须找到您将在其中使用这些 uv 通道的其余逻辑。
注:
将两个通道打包到一个属性中可能是个好主意:
attribute vec4 aUv23;
...
vUv2 = aUv23.xy;
vUv3 = aUv23.zw;
我正在尝试在 three.js 中使用两个以上的贴图通道来为我的模型分配纹理。令我惊讶的是,three.js 中的原生 materials 不支持多组 uvs。因此我必须使用 ShaderMaterial 并编写自己的 vertex/fragment 着色器才能访问多个贴图通道。
现在我确实设法让多通道 uv 与我写的一个简单代码一起工作:
<script id="vertex_shh" type="x-shader/x-vertex">
//VERTEX SHADER//
varying vec2 vUv;
attribute vec2 uv2;
varying vec2 vUv2;
attribute vec2 uv3;
varying vec2 vUv3;
attribute vec2 uv4;
varying vec2 vUv4;
attribute vec2 uv5;
varying vec2 vUv5;
void main()
{
vUv = uv;
vUv2 = uv2;
vUv3 = uv3;
vUv4 = uv4;
vUv5 = uv5;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
// END //
</script>
<script id="fragment_shh" type="x-shader/x-fragment">
//FRAGMENT SHADER//
uniform sampler2D tCh1;
uniform sampler2D tCh2;
uniform sampler2D tCh3;
uniform sampler2D tCh4;
uniform sampler2D tCh5;
varying vec2 vUv;
varying vec2 vUv2;
varying vec2 vUv3;
varying vec2 vUv4;
varying vec2 vUv5;
void main(void)
{
vec3 DiffuseComp;
vec4 TexCh1 = texture2D(tCh1, vUv);
vec4 TexCh2 = texture2D(tCh2, vUv2);
vec4 TexCh3 = texture2D(tCh3, vUv3);
vec4 TexCh4 = texture2D(tCh4, vUv4);
vec4 TexCh5 = texture2D(tCh5, vUv5);
DiffuseComp = ((TexCh1.rgb * (1.0 - TexCh4.rgb) + (TexCh2.rgb * TexCh4.rgb)) * (1.0 - TexCh5.rgb)) + (TexCh3.rgb * TexCh5.rgb);
gl_FragColor= vec4(DiffuseComp, 1.0);
}
// END //
/* SHADER */
var vertShader = document.getElementById('vertex_shh').innerHTML;
var fragShader = document.getElementById('fragment_shh').innerHTML;
var attributes = {};
var uniforms = {
tCh1: { type: "t", value: Textura1},
tCh2: { type: "t", value: Textura2},
tCh3: { type: "t", value: Textura3},
tCh4: { type: "t", value: Textura4},
tCh5: { type: "t", value: Textura5}
};
/* MATERIAL */
var multitexsh = new THREE.ShaderMaterial({
uniforms: uniforms,
attributes: attributes,
vertexShader: vertShader,
fragmentShader: fragShader
});
但是,我的问题是我基本上需要一个具有此功能的着色器和 Phong 着色器的一些基本功能:照明(来自场景灯)和镜面高光。我完全迷失在这里。我已经尝试寻找将此功能添加到本机着色器或将提到的功能从 phong material 添加到我自己的着色器的方法,但没有发现任何信息足以用我非常基本的编码技能来做到这一点。 .有人可以介入并帮助我吗?
编辑:
感谢 pailhead 让它与 onBeforeCompile 一起工作。这是在 r89 上,更新后的代码如下:
/* MANAGERS, LOADERS */
var manager = new THREE.LoadingManager();
var loader = new THREE.FBXLoader( manager );
var textureLoader = new THREE.TextureLoader( manager );
/*TEXTURE */
var prodtex ='textures/testing/cubetex.jpg';
var prodtex2 ='textures/testing/cube_blendmap.jpg';
var Textura1 = textureLoader.load (prodtex);
var Textura2 = textureLoader.load (prodtex);
var Textura3 = textureLoader.load (prodtex2);
/* MATERIAL */
var MAT = new THREE.MeshPhongMaterial();
MAT.map = Textura1;
/* MY SHADER CHANGES*/
const uv2_chunkToReplaceVS = 'attribute vec2 uv2; varying vec2 vUv2; attribute vec2 uv3; varying vec2 vUv3;'
const uv2_chunkToReplaceVSV = 'vUv2 = uv2; vUv3 = uv3;'
const uv2_chunkToReplaceFS = 'uniform sampler2D tCh1; uniform sampler2D tCh2; uniform sampler2D tCh3; varying vec2 vUv2; varying vec2 vUv3;'
const map_chunkToReplaceFSV = 'vec4 TexCh1 = texture2D(tCh1, vUv); vec4 TexCh2 = texture2D(tCh2, vUv2); vec4 TexCh3 = texture2D(tCh3, vUv3); vec4 texelColor = texture2D( map, vUv ); texelColor = mapTexelToLinear( texelColor ); TexCh1 = mapTexelToLinear( TexCh1 ); TexCh2 = mapTexelToLinear( TexCh2 ); TexCh2 = mapTexelToLinear( TexCh2 ); diffuseColor *= TexCh1 * (1.0 - TexCh3) + (TexCh2 * TexCh3);'
MAT.onBeforeCompile = shader=>{
shader.uniforms.tCh1= {type: "t", value: Textura1};
shader.uniforms.tCh2= {type: "t", value: Textura2};
shader.uniforms.tCh3= {type: "t", value: Textura3};
shader.fragmentShader = shader.fragmentShader.replace('#include <map_fragment>', map_chunkToReplaceFSV);
shader.vertexShader = shader.vertexShader.replace('#include <uv2_pars_vertex>', uv2_chunkToReplaceVS );
shader.vertexShader = shader.vertexShader.replace('#include <uv2_vertex>', uv2_chunkToReplaceVSV );
shader.fragmentShader = shader.fragmentShader.replace('#include <uv2_pars_fragment>', uv2_chunkToReplaceFS);
};
/* MODEL */
loader.load( ('models/' + 'cube' + '.FBX'), function( object ) {
//set material//
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = MAT;
};
} );
scene.add (object);
});
您可以使用 material 的一个未记录的功能,称为 onBeforeCompile
。
这是在三个解析着色器模板之前和编译着色器之前调用的回调。
模板可能如下所示
//someTemplate.vert
#include <common>
#include <some_pars_vertex>
void main(){
#include <some_vertex>
#include <another_vertex>
gl_Position = projectionMatrix * foobar;
}
假设您要添加更多 uv 通道。你会在 GLSL 中做一些 snippets/functions。这必须发生在全局范围内(void main(){...}
以上):
//myBeforeVoidMain.vert
attribute vec2 aUV2;
attribute vec2 aUV3;
varying vec2 vUv2;
varying vec2 vUv3;
然后这必须发生在主函数中:
//myUv.vert
vUv2 = aUV2;
vUv3 = aUV3;
您可以注入此代码,但在何处以及如何注入?
如何:
const chunkToReplace = THREE.ShaderChunk.uv_pars_vertex
myMaterial = new THREE.MeshPhongMaterial()
myMaterial.onBeforeCompile( shader=>{
shader.uniforms.myUniform = {value:foo}
shader.vertexShader = shader.vertexShader.replace( '#include <uv_pars_vertex>', chunkToReplace + myChunk )
})
其中:
您需要熟悉两者: https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderLib
你需要考虑一下。可以注意到 <common>
存在于几乎每个库的全局范围内。但是,因为它包含在垂直着色器和碎片着色器中,所以您不能在其中提及 attribute
,碎片着色器将无法编译。
向顶点着色器添加属性、制服和变量的一个很好的块是uv_pars_vertex
,因为它也存在于几乎所有着色器中。
对于这个特殊问题,您还必须扩展片段着色器。同样,varyings 将被放置在 uv_pars_fragment
中,然后您必须找到您将在其中使用这些 uv 通道的其余逻辑。
注:
将两个通道打包到一个属性中可能是个好主意:
attribute vec4 aUv23;
...
vUv2 = aUv23.xy;
vUv3 = aUv23.zw;