Я пытаюсь провести несколько экспериментов рендеринга 3d-сферы с использованием three.js
Цель эксперимента:
«Можем ли мы визуализировать трехмерную сферу с заданными мировыми координатами сферы (x,y,z)
и радиусом r
, используя лучевую метку?»
Эксперимент показался удачнымну как ниже. Левый красный кружок представляет собой сферу на основе three.js
и хорошо визуализируется в перспективной камере.Правый выводится с помощью raymarching с единообразными переменными, такими как
uniforms: {
x: { value: -testSphere.position.x }, // just for comparing left and right
y: { value: testSphere.position.y },
z: { value: testSphere.position.z },
r: { value: 100 }, //fixed but can be variable
u_resolution: {
type: "v2",
value: new THREE.Vector2(SCREEN_WIDTH, SCREEN_HEIGHT)
},
u_pixel_ratio: {
type: "float",
value: window.devicePixelRatio
},
campos: {
type: "v3",
value: camPos // in my case, it was (0, 0, 1000)
}
}
А также, для правильной обработки события изменения размера окна пользователя, прикрепляется обработчик событий, подобный этому.
function onWindowResize(event) {
SCREEN_WIDTH = window.innerWidth;
SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
camera.updateProjectionMatrix();
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
howBackPlane.material.uniforms.u_resolution.value = new THREE.Vector2(
SCREEN_WIDTH,
SCREEN_HEIGHT); // howBackPlane is plane mesh for draw fragment shader of right red sphere.
}
Однако, когдаЯ изменяю размер окна по горизонтали или вертикали, кажется, что правое окно не отображается так, как я ожидал.
Изменение размера по горизонтали (уменьшается) Изменение размера по вертикали (увеличивается)
Что еще хуже, каждый раз, когда я обновляю свою страницу любого размера, она возвращается к нормальной (работает правильно).Я даже не знаю, почему тот, у которого three.js
работает идеально, а почему не мой.Как сделать так, чтобы сфера отображалась как three.js
one?
Вот мой код вершинного шейдера и фрагментного шейдера howBackPlane
.
//vertex shader
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
//fragment shader
uniform vec2 u_resolution;
uniform float u_pixel_ratio;
uniform vec3 campos;
uniform float x;
uniform float y;
uniform float z;
uniform float r;
mat3 calcLookAtMatrix( in vec3 ro, in vec3 ta)
{
vec3 ww = normalize( ta - ro );
vec3 uu = normalize( cross(ww,vec3(0.0,1.0,0.0) ) );
vec3 vv = normalize( cross(uu,ww));
return mat3( uu, vv, ww );
}
float sdSphere( vec3 p, float s )
{
return length(p - vec3(x, y, z))-s;
}
float calcIntersection( in vec3 ro, in vec3 rd )
{
const float maxd = 20000.0;
const float precis = 0.01;
float h = precis*2.0;
float t = 0.0;
float res = -1.0;
for( int i=0; i<150; i++ )
{
if( h<precis||t>maxd ) break;
h = sdSphere( ro+rd*t , r);
t += h;
}
if( t<maxd ) res = t;
return res;
}
void main(){
vec3 camtar = vec3(0.0, 0.0, 0.0);
mat3 camMat = calcLookAtMatrix( campos, camtar );
vec2 p = -1.0 + 2.0 * (gl_FragCoord.xy/u_pixel_ratio) / (u_resolution.xy);
p.x *= u_resolution.x/u_resolution.y;
vec3 camdir = normalize(camMat * vec3(p, 1.0/tan(radians(45.0/2.0)))); // fov of camera is 45 deg in this app.
float dist = calcIntersection(campos, camdir);
if (dist != -1.0) {
vec3 col = vec3(0.0);
vec3 pos = campos + dist * camdir;
col += vec3(pos);
gl_FragColor = vec4(vec3(1.0, 0.0, 0.0), 1.0);
}else{
gl_FragColor = vec4(0.0);
}
}
Вы можете увидеть ресурс лучевого марширования, на который я ссылался здесь.