如何向ShaderMaterial添加不透明度贴图

时间:2020-10-30 22:48:18

标签: three.js

我已将ShaderMaterial应用于具有不透明度贴图的glb模型(该模型是人体,不透明度贴图用于创建头发和睫毛),模型材料的引用是此-

enter image description here

如您所见-该材料具有某种发光效果,因此我设法找到了This Example,这几乎是我所需要的-问题是我不知道该如何申请模型的不透明度贴图-如果您仔细观察我的结果(左图)和右图之间的差异-您会看到头发看起来不像它应该的样子-因为未应用不透明度贴图...我不知道ShaderMaterial是否适合这种外观,还是我应该使用其他种类的着色器?

enter image description here

这是我的材料代码-

  let m = new THREE.MeshStandardMaterial({
  roughness: 0.25,
  metalness: 0.75,
  opacity: 0.3,

  map: new THREE.TextureLoader().load(
    "/maps/opacity.jpg",
    (tex) => {
      tex.wrapS = THREE.RepeatWrapping;
      tex.wrapT = THREE.RepeatWrapping;
      tex.repeat.set(16, 1);
    }
  ),
  onBeforeCompile: (shader) => {
    
    shader.uniforms.s = uniforms.s;
    shader.uniforms.b = uniforms.b;
    shader.uniforms.p = uniforms.p;

    shader.uniforms.glowColor = uniforms.glowColor;
    shader.vertexShader = document.getElementById("vertexShader").textContent;
    shader.fragmentShader = document.getElementById(
      "fragmentShader"
    ).textContent;
    shader.side = THREE.FrontSide;
    shader.transparent = true;
  //  shader.uniforms['alphaMap'].value.needsUpdate = true;

    console.log(shader.vertexShader);
    console.log(shader.fragmentShader);
  },
});

着色器设置:

      <script id="vertexShader" type="x-shader/x-vertex">
    varying vec3 vNormal;
    varying vec3 vPositionNormal;
    void main() 
    {
      vNormal = normalize( normalMatrix * normal ); // 转换到视图空间
      vPositionNormal = normalize(( modelViewMatrix * vec4(position, 1.0) ).xyz);
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  </script>
  <!-- fragment shader a.k.a. pixel shader -->
  <script id="fragmentShader" type="x-shader/x-vertex"> 
    uniform vec3 glowColor;
    uniform float b;
    uniform float p;
    uniform float s;
    varying vec3 vNormal;
    varying vec3 vPositionNormal;
    void main() 
    {
      float a = pow( b + s * abs(dot(vNormal, vPositionNormal)), p );
      gl_FragColor = vec4( mix(vec3(0), glowColor, a), 1. );
    }
  </script>

1 个答案:

答案 0 :(得分:1)

您正在创建MeshStandardMaterial,但是当您分配新的顶点和片段着色器时,您将覆盖其所有着色器代码,从而使Standard材质无用。您应该像链接的演示一样坚持使用ShaderMaterial。它将使您的代码更简洁:

// Get shader code
let vertShader = document.getElementById("vertexShader").textContent;
let fragShader = document.getElementById("fragmentShader").textContent;

// Build texture
let alphaTex = new THREE.TextureLoader().load("/maps/opacity.jpg");
alphaTex.wrapS = THREE.RepeatWrapping;
alphaTex.wrapT = THREE.RepeatWrapping;
// alphaTex.repeat.set(16, 1); <- repeat won't work in a custom shader

// Build material
let m = new THREE.ShaderMaterial({
    transparent: true,
    // side: THREE.FrontSide, <- this is already default. Not needed
    uniforms: {
        s: {value: 1},
        b: {value: 2},
        p: {value: 3},
        alphaMap: {value: alphaTex},
        glowColor: {value: new THREE.Color(0x0099ff)},
        // we create a Vec2 to manually handle repeat 
        repeat: {value: new THREE.Vector2(16, 1)} 
    },
    vertexShader: vertShader,
    fragmentShader: fragShader
});

这有助于以更简洁的方式构建材质,因为您使用的是本机构建方法,而不必覆盖任何内容。然后,您可以在片段着色器中采样alphaMap纹理:

uniform float s;
uniform float b;
uniform float p;
uniform vec3 glowColor;
uniform vec2 repeat;
// Declare the alphaMap uniform if we're gonna use it
uniform sampler2D alphaMap;

// Don't forget to declare UV coordinates
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPositionNormal;
void main() 
{
  float a = pow( b + s * abs(dot(vNormal, vPositionNormal)), p );

  // Sample map with UV coordinates. Multiply by uniform to get repeat
  float a2 = texture2D(alphaMap, vUv * repeat).r;

  // Combine both alphas
  float opacity = a * a2;

  gl_FragColor = vec4( mix(vec3(0), glowColor, opacity), 1. );
}

此外,不要忘记从顶点着色器继承UV:

// Don't forget to declare UV coordinates
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPositionNormal;
void main() 
{
  // convert uv attribute to vUv varying
  vUv = uv;
  vNormal = normalize( normalMatrix * normal ); // 转换到视图空间
  vPositionNormal = normalize(( modelViewMatrix * vec4(position, 1.0) ).xyz);
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

更新

错误

'=':无法从“ lowp float的4个分量矢量”转换为“ highp float”

意味着在片段着色器中获取texture2D()示例时,我犯了一个错误。应该是texture2D().r,所以我们只读取红色通道以获得float,而不是将所有RGBA通道塞满(产生vec4)到float中。最终结果请参见以下代码片段:

var container, scene, camera, renderer, controls, torusKnot;
init()
        
function init() {
  initBase()
  initObject()
  render()
}

function initBase () {
  container = document.getElementById( 'ThreeJS' )

  // SCENE
  scene = new THREE.Scene();
  // CAMERA
  var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight
  var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000
  camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR)

  camera.position.set(0,0,50)
  camera.lookAt(scene.position)
  // RENDERER
  renderer = new THREE.WebGLRenderer( {antialias:true} )
  renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT)
  renderer.setClearColor(0x333333)
  container.appendChild( renderer.domElement )
  // CONTROLS
  controls = new THREE.OrbitControls( camera, renderer.domElement )
  
  // Resize
  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
    var w = window.innerWidth;
    var h = window.innerHeight;
        renderer.setSize(w, h);
        camera.aspect = w / h;
        camera.updateProjectionMatrix();
}


function initObject () {
  let vertShader = document.getElementById("vertexShader").textContent;
let fragShader = document.getElementById("fragmentShader").textContent;
// Build texture
let alphaTex = new THREE.TextureLoader().load("https://threejs.org/examples/textures/floors/FloorsCheckerboard_S_Diffuse.jpg");
alphaTex.wrapS = THREE.RepeatWrapping;
alphaTex.wrapT = THREE.RepeatWrapping;
  var customMaterial = new THREE.ShaderMaterial({
    uniforms: {
        s: {value: -1},
        b: {value: 1},
        p: {value: 2},
        alphaMap: {value: alphaTex},
        glowColor: {value: new THREE.Color(0x00ffff)},
        // we create a Vec2 to manually handle repeat 
        repeat: {value: new THREE.Vector2(16, 1)} 
    },
    vertexShader: vertShader,
    fragmentShader: fragShader
  })
  var geometry = new THREE.TorusKnotBufferGeometry( 10, 3, 100, 32 )
  torusKnot = new THREE.Mesh( geometry, customMaterial )
  scene.add( torusKnot )

}

function render() {
  torusKnot.rotation.y += 0.01;
  renderer.render( scene, camera );
  requestAnimationFrame(render);
}
body{
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <!-- vertext shader a.k.a. pixel shader -->
<script id="vertexShader" type="x-shader/x-vertex">
  varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPositionNormal;
void main() 
{
  // convert uv attribute to vUv varying
  vUv = uv;
  vNormal = normalize( normalMatrix * normal ); // 转换到视图空间
  vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
  vPositionNormal = normalize(( mvPosition ).xyz);
  gl_Position = projectionMatrix * mvPosition;
}
  </script>
  <!-- fragment shader a.k.a. pixel shader -->
  <script id="fragmentShader" type="x-shader/x-vertex"> 
   uniform float s;
uniform float b;
uniform float p;
uniform vec3 glowColor;
uniform vec2 repeat;
// Declare the alphaMap uniform if we're gonna use it
uniform sampler2D alphaMap;

// Don't forget to declare UV coordinates
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPositionNormal;
void main() 
{
  float a = pow( b + s * abs(dot(vNormal, vPositionNormal)), p );

  // Sample map with UV coordinates. Multiply by uniform to get repeat
  float a2 = texture2D(alphaMap, vUv * repeat).r;

  // Combine both alphas
  float opacity = a * a2;

  gl_FragColor = vec4( mix(vec3(0), glowColor, opacity), 1. );
}
  </script>

  <div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>

相关问题