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

问题描述 投票:0回答:1

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

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

实际示例实际上是此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/[email protected]/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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>
three.js webgl
1个回答
0
投票

此问题是有原因的,因为太阳位于视角的后面。请注意,在透视投影时,观看体积为Frustum。每个点都沿着光线投射通过视口上的摄影机位置。如果该点在视点后方,则它是镜像的,因为它是沿着该光线投影的。通常,这无关紧要,因为会剪切近平面前面的所有几何图形。在您的情况下,screenSpacePosition的z分量将被忽略,并且太阳不会被修剪。太阳的位置不是点,它定义了方向。因此,如果screenSpacePosition.z小于0.0,则可以省略(修剪)太阳,因为它位于相机的背面。

© www.soinside.com 2019 - 2024. All rights reserved.