我有一个使用自定义着色器在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>
问题是它没有正确点亮所以阴影等不正确。我怀疑,因为我已经改变了着色器中的顶点位置,我应该以某种方式更新顶点法线,但也许它不止于此。我尝试了各种各样的&#34; * needsUpdating = true&#34;但无济于事。我已经在three.js文档和网络上进行了处理,没有找到圣杯。
任何建议或指示都将不胜感激。
答案 0 :(得分: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>