我正在努力通过使用自定义着色器在Earth模型中使用定向光设置昼夜周期。只要我不触摸相机,夜间和白天的地图以及灯光都可以,也就是说,地球在光源保持静止的状态下旋转,并且正确地更新了夜晚和白天。但是,当我使用鼠标旋转照相机时,光线似乎跟随照相机,因此您始终会看到地球的照明部分。
这是我设置光源的方式:
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5,3,5);
scene.add(light);
这是我将参数传递给着色器的方式:
uniforms_earth = {
sunPosition: { type: "v3", value: light.position },
dayTexture: { type: "t", value: THREE.ImageUtils.loadTexture( "daymap.jpg" ) },
nightTexture: { type: "t", value: THREE.ImageUtils.loadTexture( "images/nightmap.jpg" ) }
};
这是顶点着色器:
varying vec2 v_Uv;
varying vec3 v_Normal;
uniform vec3 sunPosition;
varying vec3 v_vertToLight;
void main() {
v_Uv = uv;
v_Normal = normalMatrix * normal;
vec4 worldPosition = modelViewMatrix * vec4(position, 1.0);
v_vertToLight = normalize(sunPosition - worldPosition.xyz);
gl_Position = projectionMatrix * worldPosition;
}
这是片段着色器:
uniform sampler2D dayTexture;
uniform sampler2D nightTexture;
varying vec2 v_Uv;
varying vec3 v_Normal;
varying vec3 v_vertToLight;
void main( void ) {
vec3 dayColor = texture2D(dayTexture, v_Uv).rgb;
vec3 nightColor = texture2D(nightTexture, v_Uv).rgb;
vec3 fragToLight = normalize(v_vertToLight);
float cosineAngleSunToNormal = dot(normalize(v_Normal), fragToLight);
cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 10.0, -1.0, 1.0);
float mixAmount = cosineAngleSunToNormal * 0.5 + 0.5;
vec3 color = mix(nightColor, dayColor, mixAmount);
gl_FragColor = vec4( color, 1.0 );
}
最后,我将三个库用于相机控件:
var controls = new THREE.TrackballControls(camera);
我将渲染函数内部的地球旋转更新为:
function render() {
controls.update();
earth.rotation.y += rotation_speed;
requestAnimationFrame(render);
renderer.render(scene, camera);
}
我已经尝试更改v_vertToLight
的计算方式,以使顶点和光照位置与以下对象处于同一世界:
v_vertToLight = normalize((modelViewMatrix*vec4(sunPosition, 1.0)).xyz - worldPosition.xyz);
当我更换相机时,这阻止了光的移动,但是,夜晚的阴影总是与光开始与地球本身一起旋转的位置完全相同。
我觉得我即将解决此问题,因此任何提示或帮助将不胜感激。谢谢您的宝贵时间。
Blockquote
答案 0 :(得分:1)
您所说的worldPosition
不是世界空间中的位置,而是视图空间中的位置。重命名错误的变量:
vec4 worldPosition = modelViewMatrix * vec4(position, 1.0);
vec4 viewPosition = modelViewMatrix * vec4(position, 1.0);
sunPosition
是世界空间中的一个位置。必须先将其转换为视图空间,然后才能用于计算视图空间光矢量。这必须由viewMatrix
而非modelViewMatrix
完成。注意,modelViewMatrix
从模型空间到视图空间,而viewMatrix
从世界空间到视图空间(请参见three.js - WebGLProgram):
vec4 viewSunPos = viewMatrix * vec4(sunPosition, 1.0);
v_vertToLight = normalize(viewSunPos.xyz - viewPosition.xyz);
请注意,v_vertToLight
和v_Normal
都必须是视图空间矢量或世界空间矢量,它们必须具有相同的参考系统。否则,计算两个向量的点积就没有意义。
顶点着色器:
varying vec2 v_Uv;
varying vec3 v_Normal;
uniform vec3 sunPosition;
varying vec3 v_vertToLight;
void main() {
vec4 viewPosition = modelViewMatrix * vec4(position, 1.0);
vec4 viewSunPos = viewMatrix * vec4(sunPosition, 1.0);
v_Uv = uv;
v_Normal = normalMatrix * normal;
v_vertToLight = normalize(viewSunPos.xyz - viewPosition.xyz);
gl_Position = projectionMatrix * viewPosition;
}
请参阅使用顶点着色器的非常简单的示例:
(function onLoad() {
var loader, camera, scene, renderer, orbitControls, mesh;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(0, 1, -4);
//camera.lookAt( -1, 0, 0 );
loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = resize;
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set(1,2,1.5);
scene.add( directionalLight );
orbitControls = new THREE.OrbitControls(camera);
addGridHelper();
createModel();
}
function createModel() {
var uniforms = {
u_time : {type:'f', value:0.0},
u_resolution: {type: 'v2', value: {x:2048.,y:1024.}},
u_color : {type: 'v3', value: {x:1.0, y:0.0, z:0.0} },
sunPosition : {type: 'v3', value: {x:5.0, y:5.0, z:5.0} }
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent,
});
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, -1);
scene.add(mesh);
}
function addGridHelper() {
var helper = new THREE.GridHelper(100, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
render();
}
function render() {
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script type='x-shader/x-vertex' id='vertex-shader'>
varying vec2 v_Uv;
varying vec3 v_Normal;
uniform vec3 sunPosition;
varying vec3 v_vertToLight;
void main() {
vec4 viewPosition = modelViewMatrix * vec4(position, 1.0);
vec4 viewSunPos = viewMatrix * vec4(sunPosition, 1.0);
v_Uv = uv;
v_Normal = normalMatrix * normal;
v_vertToLight = normalize(viewSunPos.xyz - viewPosition.xyz);
gl_Position = projectionMatrix * viewPosition;
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;
uniform float u_time;
uniform vec2 u_resolution;
varying vec2 v_Uv;
varying vec3 v_Normal;
varying vec3 v_vertToLight;
uniform vec3 u_color;
void main(){
float kd = max(0.0, dot(v_vertToLight, v_Normal));
gl_FragColor = vec4(u_color.rgb * kd + 0.1, 1.0);
}
</script>