gpt4 book ai didi

javascript - 关于太阳/背景渲染的屏幕空间坐标问题

转载 作者:行者123 更新时间:2023-12-01 15:38:25 24 4
gpt4 key购买 nike

我正在尝试使用 three.js 中的自定义着色器渲染背景和太阳.这个想法是计算太阳的屏幕空间位置,并在片段着色器中使用这些坐标进行渲染。预期的行为是太阳总是在 (0,1000,-1000) 的地平线上渲染。 .当您运行实时示例并查看时,似乎情况确实如此。

但是,当您四处移动相机时(因此它沿着 (0,-1,1) 向量查看),您会注意到太阳突然镜像并沿 XY 平面翻转。为什么会这样?这是否与在着色器中计算和评估屏幕空间坐标的方法有关。

现场示例实际上是 this GitHub issue 的简化测试用例.

var container;

var camera, cameraFX, scene, sceneFX, renderer;

var uniforms;

var sunPosition = new THREE.Vector3( 0, 1000, - 1000 );
var screenSpacePosition = new THREE.Vector3();

init();
animate();

function init() {

container = document.getElementById( 'container' );

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 2000 );
camera.position.set( 0, 0, 10 );

cameraFX = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );

scene = new THREE.Scene();

scene.add( new THREE.AxesHelper( 5 ) );

sceneFX = new THREE.Scene();

var geometry = new THREE.PlaneBufferGeometry( 2, 2 );

uniforms = {
"aspect": { value: window.innerWidth / window.innerHeight },
"sunPositionScreenSpace": { value: new THREE.Vector2() }
};

var material = new THREE.ShaderMaterial( {

uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent

} );

var quad = new THREE.Mesh( geometry, material );
sceneFX.add( quad );

renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.autoClear = false;
container.appendChild( renderer.domElement );

var controls = new THREE.OrbitControls( camera, renderer.domElement );

}


//

function animate( timestamp ) {

requestAnimationFrame( animate );

renderer.clear();

// background/sun pass

screenSpacePosition.copy( sunPosition ).project( camera );

screenSpacePosition.x = ( screenSpacePosition.x + 1 ) / 2;
screenSpacePosition.y = ( screenSpacePosition.y + 1 ) / 2;

uniforms[ "sunPositionScreenSpace" ].value.copy( screenSpacePosition );

renderer.render( sceneFX, cameraFX );

// beauty pass

renderer.clearDepth();
renderer.render( scene, camera );

}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.116.1/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.116.1/examples/js/controls/OrbitControls.js"></script>

<div id="container">

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

varying vec2 vUv;

void main() {

vUv = uv;

gl_Position = vec4( position, 1.0 );

}

</script>

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

varying vec2 vUv;

uniform vec2 sunPositionScreenSpace;
uniform float aspect;

const vec3 sunColor = vec3( 1.0, 0.0, 0.0 );
const vec3 bgColor = vec3( 1.0, 1.0, 1.0 );

void main() {

vec2 diff = vUv - sunPositionScreenSpace;
diff.x *= aspect;

// background/sun drawing

float prop = clamp( length( diff ) / 0.5, 0.0, 1.0 );
prop = 0.35 * pow( 1.0 - prop, 3.0 );

gl_FragColor.rgb = mix( sunColor, bgColor, 1.0 - prop );
gl_FragColor.a = 1.0;

}

</script>

最佳答案

造成这个问题,是因为太阳是落后的观点。注意透视投影的观看量是Frustum .每个点通过视口(viewport)上的相机位置沿射线投影。如果该点位于视点后面,则它是镜像的,因为它是沿着这条射线投影的。
一般来说,这并不重要,因为近平面前面的所有几何图形都被剪裁了。

剪辑空间坐标是 Homogeneous coordinate .您必须计算剪辑空间坐标,并评估 z 分量是否小于 0。
请注意,您不能使用 Vector3.project , 因为 project计算归一化的设备空间坐标。在 NDC 中,无法区分位置是在摄像机的前面还是后面,因为在 Perspective divide 之后z 分量的单一丢失。裁剪是在裁剪空间中进行的,裁剪规则是:

-w <= x, y, z <= w. 

按均匀方向定义太阳位置:

var sunPosition = new THREE.Vector4( 0, 1, - 1, 0 );

计算裁剪空间坐标并评估 z 分量是否为负:

let clipPosition = sunPosition
.clone()
.applyMatrix4(camera.matrixWorldInverse)
.applyMatrix4(camera.projectionMatrix);

screenSpacePosition.x = ( clipPosition.x / clipPosition.w + 1 ) / 2;
screenSpacePosition.y = ( clipPosition.y / clipPosition.w + 1 ) / 2;

if (clipPosition.z < 0.0) {
// [...]
}

关于javascript - 关于太阳/背景渲染的屏幕空间坐标问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61900392/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com