2017-04-25 89 views
2

我有一个使用自定义着色器在three.js中使用高度贴图的演示。它工作正常(现在)。 jsfiddle是HERE。着色器本身在这里:如何为自定义着色器材质正确设置灯光

<script id="vertexShader" type="x-shader/x-vertex"> 
     uniform sampler2D vTexture; 
     uniform float  vScale; 
     uniform vec3  vLut[ 256 ]; 

     varying vec3  vColor; 

     void main() { 

      vec4 heightData = texture2D(vTexture, uv); 

      // if the map is grayscale it doesn't matter if you use r, g, or b. 
      float vAmount = heightData.r; 

      // fetch the color from the lookup table so it gets passed to the fragment shader 
      int index = int(heightData.r * 255.0); 
      vColor = vLut[index]; 

      // move the position along the normal 
      vec3 newPosition = position + normal * vScale * vAmount; 

      gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); 
     } 
    </script> 

    <script id="fragmentShader" type="x-shader/x-vertex"> 

     varying vec3 vColor; 

     void main() { 

      gl_FragColor = vec4(vColor, 1.0); 
     } 
    </script> 

问题是,它没有正确点亮,所以阴影等是不正确的。我怀疑,因为我已经改变了着色器中的顶点位置,所以我应该以某种方式更新顶点法线,但也许它不止于此。我尝试了各种“* needsUpdating = true”,但无济于事。我在three.js文档和网络周围浏览过,却没有找到圣杯。

任何建议或指针,将不胜感激。

回答

1

由于您使用自定义着色器,因此您需要在着色器中自己进行光照计算。这可以帮助你:Using lights in three.js shader

这里是定制照明(online demo)全样本:

<html> 
<head> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"> </script> 
    <script id="vertShader" type="shader"> 
varying vec2 vUv; 
varying vec3 vecPos; 
varying vec3 vecNormal; 

void main() { 
    vUv = uv; 
    // Since the light is in camera coordinates, 
    // I'll need the vertex position in camera coords too 
    vecPos = (modelViewMatrix * vec4(position, 1.0)).xyz; 
    // That's NOT exacly how you should transform your 
    // normals but this will work fine, since my model 
    // matrix is pretty basic 
    vecNormal = (modelViewMatrix * vec4(normal, 0.0)).xyz; 
    gl_Position = projectionMatrix * 
       vec4(vecPos, 1.0); 
} 
</script> 
    <script id="fragShader" type="shader"> 
precision highp float; 

varying vec2 vUv; 
varying vec3 vecPos; 
varying vec3 vecNormal; 

uniform float lightIntensity; 
uniform sampler2D textureSampler; 

struct PointLight { 
    vec3 color; 
    vec3 position; // light position, in camera coordinates 
    float distance; // used for attenuation purposes. Since 
        // we're writing our own shader, it can 
        // really be anything we want (as long as 
        // we assign it to our light in its 
        // "distance" field 
}; 

uniform PointLight pointLights[NUM_POINT_LIGHTS]; 

void main(void) { 
    // Pretty basic lambertian lighting... 
    vec4 addedLights = vec4(0.0, 
          0.0, 
          0.0, 
          1.0); 
    for(int l = 0; l < NUM_POINT_LIGHTS; l++) { 
     vec3 lightDirection = normalize(vecPos 
          - pointLights[l].position); 
     addedLights.rgb += clamp(dot(-lightDirection, 
           vecNormal), 0.0, 1.0) 
         * pointLights[l].color 
         * lightIntensity; 
    } 
    gl_FragColor = texture2D(textureSampler, vUv) 
       * addedLights; 
} 
</script> 
</head> 
<body style="margin: 0px;" onload="init()"> 
<script> 
    // standard global variables 
    var scene, camera, renderer, textureLoader, light; 

    // Character 3d object 
    var character = null; 

    // FUNCTIONS 
    function init() { 
    // SCENE 
    scene = new THREE.Scene(); 
    textureLoader = new THREE.TextureLoader(); 

    // CAMERA 
    var SCREEN_WIDTH = window.innerWidth; 
    var SCREEN_HEIGHT = window.innerHeight; 
    var VIEW_ANGLE = 45; 
    var ASPECT = SCREEN_WIDTH/SCREEN_HEIGHT; 
    var NEAR = 0.1; 
    var FAR = 1000; 
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, 
     NEAR, FAR); 
    scene.add(camera); 
    camera.position.set(0,0,5); 
    camera.lookAt(scene.position); 

    // RENDERER 
    renderer = new THREE.WebGLRenderer({ 
     antialias:true, 
     alpha: true 
    }); 
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); 
    var container = document.body; 
    container.appendChild(renderer.domElement); 

    // Create light 
    light = new THREE.PointLight(0xffffff, 1.0); 
    // We want it to be very close to our character 
    light.position.set(0.0, 0.0, 0.1); 
    scene.add(light); 

    // Create character 
    character = buildCharacter(); 
    scene.add(character); 

    // Start animation 
    animate(); 
    } 

    var buildCharacter = (function() { 
    var _geo = null; 

    // Share the same geometry across all planar objects 
    function getPlaneGeometry() { 
     if(_geo == null) { 
     _geo = new THREE.PlaneGeometry(1.0, 1.0); 
     } 

     return _geo; 
    }; 

    return function() { 
     var g = getPlaneGeometry(); 
     var creatureImage = textureLoader.load('g.png'); 
     creatureImage.magFilter = THREE.NearestFilter; 

     var mat = new THREE.ShaderMaterial({ 
     uniforms: THREE.UniformsUtils.merge([ 
      THREE.UniformsLib['lights'], 
      { 
      lightIntensity: {type: 'f', value: 1.0}, 
      textureSampler: {type: 't', value: null} 
      } 
     ]), 
     vertexShader: document.getElementById('vertShader').text, 
     fragmentShader: document.getElementById('fragShader').text, 
     transparent: true, 
     lights: true 
     }); 
     // THREE.UniformsUtils.merge() call THREE.clone() on 
     // each uniform. We don't want our texture to be 
     // duplicated, so I assign it to the uniform value 
     // right here. 
     mat.uniforms.textureSampler.value = creatureImage; 

     var obj = new THREE.Mesh(g, mat); 

     return obj; 
    } 
    })(); 

    function animate() { 
    // Update light profile 
    var timestampNow = new Date().getTime()/1000.0; 
    var lightIntensity = 0.75 + 
     0.25 * Math.cos(timestampNow * 
     Math.PI); 

    character.material.uniforms 
     .lightIntensity.value = lightIntensity; 
    light.color.setHSL(lightIntensity, 1.0, 0.5); 

    // Render scene 
    renderer.render(scene, camera); 
    requestAnimationFrame(animate); 
    } 
</script> 
</body> 
</html> 
+0

谢谢,但我还是不明白这一点。我周围发现并发现了一大堆相互矛盾的信息。我更新了我的小提琴[这里](http://jsfiddle.net/richardylewright/7nwpob4y/32/)与另一个尝试在创建shaderMaterial和包括灯。生成的shaderMaterial(在调试器中)看起来没问题。所有合适的制服人员都在那里(从灯光库)以及我的定制制服(lut等)。它没有错误地运行,但飞机没有得到高度信息,它是黑色的。猜测,顶点着色器中止了?任何人都可以指出我缺少的东西吗? TIA。 – rkwright

+0

感谢您的帮助!我复制它并运行它(伪造一个g.png后)。有用。所以我现在正在研究代码。然而,我已经阅读了一些关于SO的文章,建议可以“简单地”将场景灯光传递给着色器,而不必在着色器中重新创建灯光。看[这里](http://stackoverflow.com/questions/35596705/using-lights-in-three-js-shader),但也许我误解了。 – rkwright

相关问题