WebGL - глобус с ShaderMaterial
У меня проблема при повороте дневного шара, созданного ShaderMaterial. Когда я поворачиваю шар, дневная зона не смещается в направлении солнца.
// Globe Sun setup
start_time = getUTCTime() * rotateTime;
var sunDirection = new THREE.Vector3(1.0, 0.0, 0.0);
sunDirection.x = Math.cos(start_time);
sunDirection.y = 0.0;
sunDirection.z = Math.sin(start_time);
uniforms = {
sunDirection: {type: "v3", value: sunDirection},
dayTexture: {type: "t", value: new THREE.TextureLoader().load("globe/img/earth-day.jpg")},
nightTexture: {type: "t", value: new THREE.TextureLoader().load("globe/img/earth-night.jpg")}
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
var geometry = new THREE.SphereGeometry(globeRadius, 64, 64);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
Вот код GSTL
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D dayTexture;
uniform sampler2D nightTexture;
uniform vec3 sunDirection;
varying vec2 vUv;
varying vec3 vNormal;
void main( void ) {
vec4 dayColor = texture2D(dayTexture, vUv);
vec4 nightColor = texture2D(nightTexture, vUv);
// compute cosine sun to normal so -1 is away from sun and +1 is toward sun.
float cosineAngleSunToNormal = dot(normalize(vNormal), sunDirection);
// sharpen the edge between the transition
cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 10.0, -1.0, 3.0);
// convert to 0 to 1 for mixing
float mixAmount = cosineAngleSunToNormal * 0.5 + 0.5;
// Select day or night texture based on mix.
vec4 color = mix(nightColor, dayColor, mixAmount);
gl_FragColor = color;
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
varying vec3 vNormal;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vNormal = normalMatrix * normal;
gl_Position = projectionMatrix * mvPosition;
}
</script>
Область дневной ночи будет установлена в соответствии с текущим временем UTC. Когда я поворачиваю глобус, вращается только глобус, область солнца не перемещается. Вот пример, который я хочу сделать как.
1 ответ
Чтобы сделать то, что вы хотите, вы должны преобразовать направление на солнце путем ориентации матрицы вида модели. Эта матрица ориентации 3*3 называется нормальной матрицей и может быть рассчитана путем обратного транспонирования верхних левых 3*3 элементов матрицы вида модели.
К счастью, THREE.js предоставляет встроенную форму, включая эту нормальную матрицу. См. WebGLProgram - Встроенная униформа.
Все, что вам нужно сделать, это объявить единую переменную с именем normalMatrix
типа mat3
и преобразовать sunDirection
по этой матрице во фрагментном шейдере:
uniform sampler2D dayTexture;
uniform sampler2D nightTexture;
uniform mat3 normalMatrix;
uniform vec3 sunDirection;
varying vec2 vUv;
varying vec3 vNormal;
void main( void ) {
vec4 dayColor = texture2D(dayTexture, vUv);
vec4 nightColor = texture2D(nightTexture, vUv);
vec3 dir = normalize(normalMatrix * sunDirection);
// compute cosine sun to normal so -1 is away from sun and +1 is toward sun.
float cosineAngleSunToNormal = dot(normalize(vNormal), dir);
// sharpen the edge between the transition
cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 10.0, -1.0, 3.0);
// convert to 0 to 1 for mixing
float mixAmount = cosineAngleSunToNormal * 0.5 + 0.5;
// Select day or night texture based on mix.
vec4 color = mix(nightColor, dayColor, mixAmount);
gl_FragColor = color;
}
Если вы хотите изменить форму sunDirection
само по себе, то достаточно изменить THREE.Vector3
что соответствует униформе.
например, создать анимацию дня и ночи, последовательно манипулируя направлением солнца в render
функция:
var time = 0.0;
var sunDirection = new THREE.Vector3(1.0, 0.0, 0.0);
function render() {
time += 0.03;
sunDirection.set(Math.cos(time), 0.0, Math.sin(time))
renderer.render(scene, camera);
}
Смотрите пример:
(function onLoad() {
var container, loader, camera, scene, renderer, orbitControls;
var time = 0.0;
var sunDirection = new THREE.Vector3(1.0, 0.0, 0.0);
init();
animate();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(0, 0, -2.5);
loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.add(camera);
window.onresize = resize;
orbitControls = new THREE.OrbitControls(camera);
createModel();
}
function createModel() {
var globeRadius = 1.0;
uniforms = {
sunDirection: {type: "v3", value: sunDirection},
dayTexture: {type: "t", value: new THREE.TextureLoader().load("https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/worldmap_day.png")},
nightTexture: {type: "t", value: new THREE.TextureLoader().load("https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/worldmap_night.png")}
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
var geometry = new THREE.SphereGeometry(globeRadius, 64, 64);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
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() {
time += 0.03;
sunDirection.set(Math.cos(time), 0.0, Math.sin(time))
renderer.render(scene, camera);
}
})();
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D dayTexture;
uniform sampler2D nightTexture;
uniform mat3 normalMatrix;
uniform vec3 sunDirection;
varying vec2 vUv;
varying vec3 vNormal;
void main( void ) {
vec4 dayColor = texture2D(dayTexture, vUv);
vec4 nightColor = texture2D(nightTexture, vUv);
vec3 dir = normalize(normalMatrix * sunDirection);
// compute cosine sun to normal so -1 is away from sun and +1 is toward sun.
float cosineAngleSunToNormal = dot(normalize(vNormal), dir);
// sharpen the edge between the transition
cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 10.0, -1.0, 3.0);
// convert to 0 to 1 for mixing
float mixAmount = cosineAngleSunToNormal * 0.5 + 0.5;
// Select day or night texture based on mix.
vec4 color = mix(nightColor, dayColor, mixAmount);
gl_FragColor = color;
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
varying vec3 vNormal;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vNormal = normalMatrix * normal;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>