#javascript #three.js #post-processing
#язык JavaScript #three.js #постобработка
Вопрос:
Я пробую свои силы в постобработке. Моя цель состоит в том, чтобы поместить сферу, сгенерированную в шейдере постобработки, поверх сферы в сцене. но эти две сферы не хотят быть одинаковыми. всегда есть различия. в шейдер включен параметр raymarcher (viewDir), который создает сферу с функцией raySphere. однако для этого я использую УФ-координаты в шейдере. Я думаю, что моя проблема в том, что я не знаю, как связать мировые координаты и мировую камеру с УФ-координатами. текстура tdepht вторична, она работает. Цель состоит в том, чтобы в шейдере постобработки была сфера, которая всегда находится над сферой в сцене.
import Three from '../lib/three/build/three.js'; import * as THREE from "../lib/three/build/three.module.js"; import { OrbitControls } from '../lib/three/examples/jsm/controls/OrbitControls.js'; import { EffectComposer } from '../lib/three/examples/jsm/postprocessing/EffectComposer.js'; import { RenderPass } from '../lib/three/examples/jsm/postprocessing/RenderPass.js'; import { ShaderPass } from '../lib/three/examples/jsm/postprocessing/ShaderPass.js'; class Main { constructor(){ this.renderer; this.container; this.camera; this.scene; this.controls; this.composer; this.sphere; this.sphereGeo; this.depthRenderTarget; this.depthShader; this.init(); this.animate(); } init(){ this.renderer = new THREE.WebGLRenderer( { antialias: true } ); this.renderer.setPixelRatio( window.devicePixelRatio ); this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; this.container = document.getElementById('container'); this.renderer.setSize(this.container.clientWidth, this.container.clientHeight); this.container.appendChild( this.renderer.domElement ); var aspect = this.container.clientWidth / this.container.clientHeight; this.scene = new THREE.Scene(); this.scene.background = new THREE.Color( 0x000000 ); this.camera = new THREE.PerspectiveCamera( 45, aspect, 1, 10000000 ); this.camera.position.set(0, 0, 10000); this.controls = new OrbitControls( this.camera, this.renderer.domElement ); this.controls.enableZoom = true; this.controls.enabled = true; this.controls.target.set(0, 0, 0); this.composer = new EffectComposer( this.renderer ); this.renderPass = new RenderPass( this.scene, this.camera ); this.composer.addPass( this.renderPass ); //------------- var ambientlight = new THREE.AmbientLight( 0xF0F0F0 ); // soft white light this.scene.add( ambientlight ); const radius = 1000 ; //var texture = new THREE.TextureLoader().load("img/earthmap.jpg"); this.sphereGeo = new THREE.SphereBufferGeometry( radius, 64, 64 ); //const material = new THREE.MeshPhysicalMaterial({ map: texture,}); const material = new THREE.MeshBasicMaterial( { color: 0x777777 } ); this.sphere = new THREE.Mesh( this.sphereGeo, material ); this.scene.add( this.sphere ); this.depthRenderTarget = new THREE.WebGLRenderTarget(this.container.clientWidth, this.container.clientHeight); this.depthRenderTarget.texture.format = THREE.RGBFormat; this.depthRenderTarget.texture.minFilter = THREE.NearestFilter; this.depthRenderTarget.texture.magFilter = THREE.NearestFilter; this.depthRenderTarget.texture.generateMipmaps = false; this.depthRenderTarget.stencilBuffer = false; this.depthRenderTarget.depthBuffer = true; this.depthRenderTarget.depthTexture = new THREE.DepthTexture(); this.depthRenderTarget.depthTexture.type = THREE.UnsignedShortType; this.depthRenderTarget.depthTexture.format = THREE.DepthFormat; this.depthShader = { uniforms: { 'tDiffuse': { value: null }, 'tDepth': { value: null }, 'aspect': { value: aspect }, 'cameraPos': {value: this.camera.position }, 'cameraNear': { value: this.camera.near }, 'cameraFar': { value: this.camera.far }, 'cameraFov': { value: this.camera.fov }, 'screenpos': {value: this.projector(this.sphere)}, 'sphereradius': { value: radius }, }, vertexShader:` varying vec2 vUv; void main() { vUv = uv; gl_Position = vec4( (uv - 0.5)*2.0, 0.0, 1.0 ); }`, fragmentShader:` #include lt;packinggt; #define PI 3.1415926535897932384626433832795 uniform sampler2D tDiffuse; uniform sampler2D tDepth; uniform float cameraNear; uniform float cameraFar; uniform float cameraFov; uniform float aspect; uniform vec3 cameraPos; uniform vec3 screenpos; uniform float sphereradius; varying vec2 vUv; float readDepth( sampler2D depthSampler, vec2 coord ) { float fragCoordZ = texture2D( depthSampler, coord ).x; float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar ); return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar ); } float adjust(float fov, float depth){ float vfov = fov*PI/180.; float result = 2.*tan(vfov/2.)*depth;//length(camPos - depth); return result; } vec2 raySphere(vec3 center, vec3 rayOrigin, vec3 rayDir, float radius){ vec3 offset = rayOrigin - center; float a = dot(rayDir, rayDir); float b = 2.0 * dot(offset, rayDir); float c = dot(offset, offset) - radius*radius; float d = b*b - 4.0*a*c; if(d gt; 0.0){ float s = sqrt(d); float dstToSphereNear = - b-s; float dstToSphereFar = -b s; if(dstToSphereFar lt; 0.0){ dstToSphereFar = -1.0; } if(dstToSphereNear lt; 0.0){ dstToSphereNear = -1.0; } dstToSphereNear = dstToSphereNear/(2.0*a); dstToSphereFar = dstToSphereFar/(2.0*a); return vec2(dstToSphereNear, dstToSphereFar - dstToSphereNear); } return vec2(-1.0, 0.0); } void main() { float depth = readDepth(tDepth, vUv); vec4 color = texture2D(tDiffuse, vUv); vec3 viewOrigin = vec3(0., 0., 1.); //I think here is the problem. I use the vUv coordinates from the flat postprocessing plane //But i have no clue how i can connect the worldcoordinates from the scene camera with this vUv coordinates from the postprocessing shader. vec3 viewDir = vec3((vUv.x-0.5)*aspect, (vUv.y-0.5), -1.); float adj = adjust(cameraFov, screenpos.z); vec3 planetcenter = vec3(screenpos.x/2.*aspect , screenpos.y/2., 0.); vec2 hitinfo = raySphere(planetcenter, viewOrigin, viewDir, sphereradius/adj); float dst = hitinfo.y/(2.*sphereradius/adj); //gl_FragColor.rgb = 1.0 - vec3( depth ); gl_FragColor = color vec4(0.,1.,0.,1.)*dst; }` }; //create a custom postprocessing with the shader this.depthPass = new ShaderPass( this.depthShader ); this.depthPass.uniforms.cameraNear.value = this.camera.near; this.depthPass.uniforms.cameraFar.value = this.camera.far; this.depthPass.uniforms.tDepth.value = this.depthRenderTarget.depthTexture; this.composer.addPass(this.depthPass); this.depthPass.renderToScreen =true; }//end init animate(){ requestAnimationFrame( this.animate.bind(this) ); this.render(); } render(){ this.controls.update(); this.camera.updateMatrixWorld(); this.camera.updateProjectionMatrix(); this.depthPass.uniforms.screenpos.value = this.projector(this.sphere); this.renderer.setRenderTarget( this.depthRenderTarget ); this.renderer.render( this.scene, this.camera ); this.composer.render(); } projector(obj){ var pos = new THREE.Vector3(); pos = pos.setFromMatrixPosition(obj.matrixWorld); pos.project(this.camera); var viewDir = new THREE.Vector3(); this.camera.getWorldDirection(viewDir); var a = new THREE.Vector3(); var b = new THREE.Vector3(); a.copy(this.camera.position); b.copy(this.sphere.position); var distance = a.distanceTo(b); var cosab = viewDir.dot((b.sub(a)).normalize()); var depth = distance*cosab; pos.z = depth; return pos; } } export default Main; /*the projector function gives the coordinates xy from the sphere in screencoordinates between -1 and 1. the z value gives the depth in worldcoordinates. the depth is neccessary to calculate the size in the postprocessingshader with the adjust function.*/ // project the xy position to screencoordinates // | // __________O lt;--sphere // | / // | / // | / // depth | / // between --gt; | / lt;--distance between camera and sphere // camera | / // and | / // sphere |/ // in world [ ] lt;--camera // distance