未验证 提交 cb9f8d0e 编写于 作者: V Vis 提交者: GitHub

Add SSRrPass (screen space refraction) (#21420)

* SSRPassCorrectReflectorDepth

* Fix? or bypass? large groundReflector edge viewing problem.

* Renaming & some clean up.

* SSRr init copy from SSR

* ok: rename SSR to SSRr & remove groundReflector

* clean up

* clean: blur

* a

* a

* pok

* debug_effect

* pok

* pok

* doing

* doing

* add gui ior

* ok

* screenshot

* bugfix: Correct transparent object z-depth occlusion.

* add vconsole

* Add specular.

* Generalize & Clean up.

* Click toggle transparent.

* Rename: specularRenderTarget.
Fix: scene.background.
MouseMove Check: Zero is sufficient.

* Correct IOR.

* Fix: Tiny gaps display error.
Rename: tSpecular.

* Clean up.

* Rename: Metalness to Refractive.
Add: Specular togglable.

* Make `defines` all uppercase.

* Add `Specular` OUTPUT.

* BugFix: Corrent `tDepthSelects`.
Add: `DepthSelects` OUTPUT.

* Rename: `normal` to `normalSelects`.

* Let `away` consider `surfDist`.

* Setting: `maxDistance` configurable.

* Use `pointToLineDistance`.

* All use `NearestFilter`.

* Add: `infiniteThick`.

* Increase `maxDistance`.

* Add setting: fillHole.
上级 6b1c5158
......@@ -262,6 +262,7 @@
"webgl_postprocessing_sobel",
"webgl_postprocessing_ssao",
"webgl_postprocessing_ssr",
"webgl_postprocessing_ssrr",
"webgl_postprocessing_taa",
"webgl_postprocessing_unreal_bloom",
"webgl_postprocessing_unreal_bloom_selective"
......
import {
AddEquation,
Color,
NormalBlending,
DepthTexture,
SrcAlphaFactor,
OneMinusSrcAlphaFactor,
MeshNormalMaterial,
MeshBasicMaterial,
NearestFilter,
NoBlending,
RGBAFormat,
ShaderMaterial,
UniformsUtils,
UnsignedShortType,
WebGLRenderTarget,
HalfFloatType,
MeshStandardMaterial
} from '../../../build/three.module.js';
import { Pass } from '../postprocessing/Pass.js';
import { SSRrShader } from '../shaders/SSRrShader.js';
import { SSRrDepthShader } from '../shaders/SSRrShader.js';
import { CopyShader } from '../shaders/CopyShader.js';
var SSRrPass = function ( { renderer, scene, camera, width, height, selects, encoding, morphTargets = false } ) {
Pass.call( this );
this.width = ( width !== undefined ) ? width : 512;
this.height = ( height !== undefined ) ? height : 512;
this.clear = true;
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.output = 0;
// this.output = 1;
this.ior = SSRrShader.uniforms.ior.value;
this.maxDistance = SSRrShader.uniforms.maxDistance.value;
this.surfDist = SSRrShader.uniforms.surfDist.value;
this.encoding = encoding;
this.tempColor = new Color();
this.selects = selects;
this._specular = SSRrShader.defines.SPECULAR;
Object.defineProperty( this, 'specular', {
get() {
return this._specular;
},
set( val ) {
if ( this._specular === val ) return;
this._specular = val;
this.ssrrMaterial.defines.SPECULAR = val;
this.ssrrMaterial.needsUpdate = true;
}
} );
this._fillHole = SSRrShader.defines.FILL_HOLE;
Object.defineProperty( this, 'fillHole', {
get() {
return this._fillHole;
},
set( val ) {
if ( this._fillHole === val ) return;
this._fillHole = val;
this.ssrrMaterial.defines.FILL_HOLE = val;
this.ssrrMaterial.needsUpdate = true;
}
} );
this._infiniteThick = SSRrShader.defines.INFINITE_THICK;
Object.defineProperty( this, 'infiniteThick', {
get() {
return this._infiniteThick;
},
set( val ) {
if ( this._infiniteThick === val ) return;
this._infiniteThick = val;
this.ssrrMaterial.defines.INFINITE_THICK = val;
this.ssrrMaterial.needsUpdate = true;
}
} );
// beauty render target with depth buffer
var depthTexture = new DepthTexture();
depthTexture.type = UnsignedShortType;
depthTexture.minFilter = NearestFilter;
depthTexture.magFilter = NearestFilter;
this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
depthTexture: depthTexture,
depthBuffer: true
} );
this.specularRenderTarget = new WebGLRenderTarget( this.width, this.height, { // TODO: Can merge with refractiveRenderTarget?
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
} );
// normalSelects render target
var depthTextureSelects = new DepthTexture();
depthTextureSelects.type = UnsignedShortType;
depthTextureSelects.minFilter = NearestFilter;
depthTextureSelects.magFilter = NearestFilter;
this.normalSelectsRenderTarget = new WebGLRenderTarget( this.width, this.height, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat,
type: HalfFloatType,
depthTexture: depthTextureSelects,
depthBuffer: true
} );
// refractive render target
this.refractiveRenderTarget = new WebGLRenderTarget( this.width, this.height, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat
} );
// ssrr render target
this.ssrrRenderTarget = new WebGLRenderTarget( this.width, this.height, {
minFilter: NearestFilter,
magFilter: NearestFilter,
format: RGBAFormat
} );
// ssrr material
if ( SSRrShader === undefined ) {
console.error( 'THREE.SSRrPass: The pass relies on SSRrShader.' );
}
this.ssrrMaterial = new ShaderMaterial( {
defines: Object.assign( {}, SSRrShader.defines, {
MAX_STEP: Math.sqrt( this.width * this.width + this.height * this.height )
} ),
uniforms: UniformsUtils.clone( SSRrShader.uniforms ),
vertexShader: SSRrShader.vertexShader,
fragmentShader: SSRrShader.fragmentShader,
blending: NoBlending
} );
this.ssrrMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
this.ssrrMaterial.uniforms[ 'tSpecular' ].value = this.specularRenderTarget.texture;
this.ssrrMaterial.uniforms[ 'tNormalSelects' ].value = this.normalSelectsRenderTarget.texture;
this.ssrrMaterial.needsUpdate = true;
this.ssrrMaterial.uniforms[ 'tRefractive' ].value = this.refractiveRenderTarget.texture;
this.ssrrMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture;
this.ssrrMaterial.uniforms[ 'tDepthSelects' ].value = this.normalSelectsRenderTarget.depthTexture;
this.ssrrMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
this.ssrrMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
this.ssrrMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
this.ssrrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
this.ssrrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
// normal material
this.normalMaterial = new MeshNormalMaterial( { morphTargets } );
this.normalMaterial.blending = NoBlending;
// refractiveOn material
this.refractiveOnMaterial = new MeshBasicMaterial( {
color: 'white'
} );
// refractiveOff material
this.refractiveOffMaterial = new MeshBasicMaterial( {
color: 'black'
});
// specular material
this.specularMaterial = new MeshStandardMaterial({
color: 'black',
metalness: 0,
roughness: .2,
});
// material for rendering the depth
this.depthRenderMaterial = new ShaderMaterial( {
defines: Object.assign( {}, SSRrDepthShader.defines ),
uniforms: UniformsUtils.clone( SSRrDepthShader.uniforms ),
vertexShader: SSRrDepthShader.vertexShader,
fragmentShader: SSRrDepthShader.fragmentShader,
blending: NoBlending
} );
this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture;
this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
// material for rendering the content of a render target
this.copyMaterial = new ShaderMaterial( {
uniforms: UniformsUtils.clone( CopyShader.uniforms ),
vertexShader: CopyShader.vertexShader,
fragmentShader: CopyShader.fragmentShader,
transparent: true,
depthTest: false,
depthWrite: false,
blendSrc: SrcAlphaFactor,
blendDst: OneMinusSrcAlphaFactor,
blendEquation: AddEquation,
blendSrcAlpha: SrcAlphaFactor,
blendDstAlpha: OneMinusSrcAlphaFactor,
blendEquationAlpha: AddEquation,
// premultipliedAlpha:true,
} );
this.fsQuad = new Pass.FullScreenQuad( null );
this.originalClearColor = new Color();
};
SSRrPass.prototype = Object.assign( Object.create( Pass.prototype ), {
constructor: SSRrPass,
dispose: function () {
// dispose render targets
this.beautyRenderTarget.dispose();
this.specularRenderTarget.dispose();
this.normalSelectsRenderTarget.dispose();
this.refractiveRenderTarget.dispose();
this.ssrrRenderTarget.dispose();
// dispose materials
this.normalMaterial.dispose();
this.refractiveOnMaterial.dispose();
this.refractiveOffMaterial.dispose();
this.copyMaterial.dispose();
this.depthRenderMaterial.dispose();
// dipsose full screen quad
this.fsQuad.dispose();
},
render: function ( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) {
// render beauty and depth
if ( this.encoding ) this.beautyRenderTarget.texture.encoding = this.encoding;
renderer.setRenderTarget( this.beautyRenderTarget );
renderer.clear();
this.scene.children.forEach(child => {
if (this.selects.includes(child)) {
child.visible = false
} else {
child.visible = true
}
})
renderer.render(this.scene, this.camera);
renderer.setRenderTarget( this.specularRenderTarget );
renderer.clear();
this.scene.children.forEach(child => {
if (this.selects.includes(child)) {
child.visible=true
child._SSRrPassBackupMaterial = child.material
child.material=this.specularMaterial
} else if(!child.isLight) {
child.visible = false
}
})
renderer.render(this.scene, this.camera);
this.scene.children.forEach(child => {
if (this.selects.includes(child)) {
child.material=child._SSRrPassBackupMaterial
}
})
// render normalSelectss
this.scene.children.forEach(child => {
if (this.selects.includes(child)) {
child.visible=true
} else{
child.visible = false
}
})
this.renderOverride(renderer, this.normalMaterial, this.normalSelectsRenderTarget, 0, 0);
this.renderRefractive( renderer, this.refractiveOnMaterial, this.refractiveRenderTarget, 0, 0 );
// render SSRr
this.ssrrMaterial.uniforms[ 'ior' ].value = this.ior;
this.ssrrMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance;
this.ssrrMaterial.uniforms[ 'surfDist' ].value = this.surfDist;
this.ssrrMaterial.uniforms[ 'tSpecular' ].value = this.specularRenderTarget.texture;
this.renderPass( renderer, this.ssrrMaterial, this.ssrrRenderTarget );
// output result to screen
switch ( this.output ) {
case SSRrPass.OUTPUT.Default:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrrRenderTarget.texture;
this.copyMaterial.blending = NormalBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.SSRr:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrrRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.Beauty:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.Depth:
this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture;
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.DepthSelects:
this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.normalSelectsRenderTarget.depthTexture;
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.NormalSelects:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalSelectsRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.Refractive:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.refractiveRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
case SSRrPass.OUTPUT.Specular:
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.specularRenderTarget.texture;
this.copyMaterial.blending = NoBlending;
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
break;
default:
console.warn( 'THREE.SSRrPass: Unknown output type.' );
}
},
renderPass: function ( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) {
// save original state
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
var originalClearAlpha = renderer.getClearAlpha( this.tempColor );
var originalAutoClear = renderer.autoClear;
renderer.setRenderTarget( renderTarget );
// setup pass state
renderer.autoClear = false;
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
renderer.setClearColor( clearColor );
renderer.setClearAlpha( clearAlpha || 0.0 );
renderer.clear();
}
this.fsQuad.material = passMaterial;
this.fsQuad.render( renderer );
// restore original state
renderer.autoClear = originalAutoClear;
renderer.setClearColor( this.originalClearColor );
renderer.setClearAlpha( originalClearAlpha );
},
renderOverride: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
var originalClearAlpha = renderer.getClearAlpha( this.tempColor );
var originalAutoClear = renderer.autoClear;
renderer.setRenderTarget( renderTarget );
renderer.autoClear = false;
clearColor = overrideMaterial.clearColor || clearColor;
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
renderer.setClearColor( clearColor );
renderer.setClearAlpha( clearAlpha || 0.0 );
renderer.clear();
}
this.scene.overrideMaterial = overrideMaterial;
renderer.render( this.scene, this.camera );
this.scene.overrideMaterial = null;
// restore original state
renderer.autoClear = originalAutoClear;
renderer.setClearColor( this.originalClearColor );
renderer.setClearAlpha( originalClearAlpha );
},
renderRefractive: function ( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) {
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) );
var originalClearAlpha = renderer.getClearAlpha( this.tempColor );
var originalAutoClear = renderer.autoClear;
renderer.setRenderTarget( renderTarget );
renderer.autoClear = false;
clearColor = overrideMaterial.clearColor || clearColor;
clearAlpha = overrideMaterial.clearAlpha || clearAlpha;
if ( ( clearColor !== undefined ) && ( clearColor !== null ) ) {
renderer.setClearColor( clearColor );
renderer.setClearAlpha( clearAlpha || 0.0 );
renderer.clear();
}
this.scene.children.forEach(child => {
child.visible=true
})
this.scene.traverse( child => {
child._SSRrPassBackupMaterial = child.material;
if ( this.selects.includes( child ) ) {
child.material = this.refractiveOnMaterial;
} else {
child.material = this.refractiveOffMaterial;
}
});
this.scene._SSRrPassBackupBackground=this.scene.background
this.scene.background=null
this.scene._SSRrPassBackupFog=this.scene.fog
this.scene.fog=null
renderer.render(this.scene, this.camera);
this.scene.fog=this.scene._SSRrPassBackupFog
this.scene.background=this.scene._SSRrPassBackupBackground
this.scene.traverse( child => {
child.material = child._SSRrPassBackupMaterial;
} );
// restore original state
renderer.autoClear = originalAutoClear;
renderer.setClearColor( this.originalClearColor );
renderer.setClearAlpha( originalClearAlpha );
},
setSize: function ( width, height ) {
this.width = width;
this.height = height;
this.ssrrMaterial.defines.MAX_STEP = Math.sqrt( width * width + height * height );
this.ssrrMaterial.needsUpdate = true;
this.beautyRenderTarget.setSize( width, height );
this.specularRenderTarget.setSize( width, height );
this.ssrrRenderTarget.setSize( width, height );
this.normalSelectsRenderTarget.setSize( width, height );
this.refractiveRenderTarget.setSize( width, height );
this.ssrrMaterial.uniforms[ 'resolution' ].value.set( width, height );
this.ssrrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
this.ssrrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
},
} );
SSRrPass.OUTPUT = {
'Default': 0,
'SSRr': 1,
'Beauty': 3,
'Depth': 4,
'DepthSelects': 9,
'NormalSelects': 5,
'Refractive': 7,
'Specular': 8,
};
export { SSRrPass };
import {
Matrix4,
Vector2
} from "../../../build/three.module.js";
var SSRrShader = {
defines: {
MAX_STEP: 0,
PERSPECTIVE_CAMERA: true,
SPECULAR: true,
FILL_HOLE: true,
INFINITE_THICK: false,
},
uniforms: {
"tDiffuse": { value: null },
"tSpecular": { value: null },
"tNormalSelects": { value: null },
"tRefractive": { value: null },
"tDepth": { value: null },
"tDepthSelects": { value: null },
"cameraNear": { value: null },
"cameraFar": { value: null },
"resolution": { value: new Vector2() },
"cameraProjectionMatrix": { value: new Matrix4() },
"cameraInverseProjectionMatrix": { value: new Matrix4() },
"ior": { value: 1.03 },
"cameraRange": { value: 0 },
"maxDistance": { value: 180 },
"surfDist": { value: .007 },
},
vertexShader: /* glsl */`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: /* glsl */`
// precision highp float;
precision highp sampler2D;
varying vec2 vUv;
uniform sampler2D tDepth;
uniform sampler2D tDepthSelects;
uniform sampler2D tNormalSelects;
uniform sampler2D tRefractive;
uniform sampler2D tDiffuse;
uniform sampler2D tSpecular;
uniform float cameraRange;
uniform vec2 resolution;
uniform float cameraNear;
uniform float cameraFar;
uniform float ior;
uniform mat4 cameraProjectionMatrix;
uniform mat4 cameraInverseProjectionMatrix;
uniform float maxDistance;
uniform float surfDist;
#include <packing>
float pointToLineDistance(vec3 x0, vec3 x1, vec3 x2) {
//x0: point, x1: linePointA, x2: linePointB
//https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
return length(cross(x0-x1,x0-x2))/length(x2-x1);
}
float pointPlaneDistance(vec3 point,vec3 planePoint,vec3 planeNormal){
// https://mathworld.wolfram.com/Point-PlaneDistance.html
//// https://en.wikipedia.org/wiki/Plane_(geometry)
//// http://paulbourke.net/geometry/pointlineplane/
float a=planeNormal.x,b=planeNormal.y,c=planeNormal.z;
float x0=point.x,y0=point.y,z0=point.z;
float x=planePoint.x,y=planePoint.y,z=planePoint.z;
float d=-(a*x+b*y+c*z);
float distance=(a*x0+b*y0+c*z0+d)/sqrt(a*a+b*b+c*c);
return distance;
}
float getDepth( const in vec2 uv ) {
return texture2D( tDepth, uv ).x;
}
float getDepthSelects( const in vec2 uv ) {
return texture2D( tDepthSelects, uv ).x;
}
float getViewZ( const in float depth ) {
#ifdef PERSPECTIVE_CAMERA
return perspectiveDepthToViewZ( depth, cameraNear, cameraFar );
#else
return orthographicDepthToViewZ( depth, cameraNear, cameraFar );
#endif
}
vec3 getViewPosition( const in vec2 uv, const in float depth/*clip space*/, const in float clipW ) {
vec4 clipPosition = vec4( ( vec3( uv, depth ) - 0.5 ) * 2.0, 1.0 );//ndc
clipPosition *= clipW; //clip
return ( cameraInverseProjectionMatrix * clipPosition ).xyz;//view
}
vec3 getViewNormalSelects( const in vec2 uv ) {
return unpackRGBToNormal( texture2D( tNormalSelects, uv ).xyz );
}
vec2 viewPositionToXY(vec3 viewPosition){
vec2 xy;
vec4 clip=cameraProjectionMatrix*vec4(viewPosition,1);
xy=clip.xy;//clip
float clipW=clip.w;
xy/=clipW;//NDC
xy=(xy+1.)/2.;//uv
xy*=resolution;//screen
return xy;
}
void setResultColor(vec2 uv){
vec4 refractColor=texture2D(tDiffuse,uv);
#ifdef SPECULAR
vec4 specularColor=texture2D(tSpecular,vUv);
gl_FragColor.xyz=mix(refractColor.xyz,vec3(1),specularColor.r);
// gl_FragColor.xyz=refractColor.xyz*(1.+specularColor.r*3.);
#else
gl_FragColor.xyz=refractColor.xyz;
#endif
gl_FragColor.a=1.;
}
void main(){
if(ior==1.) return; // Adding this line may have better performance, but more importantly, it can avoid display errors at the very edges of the model when IOR is equal to 1.
float refractive=texture2D(tRefractive,vUv).r;
if(refractive<=0.) return;
// gl_FragColor=vec4(0,0,.5,1);return;
vec3 viewNormalSelects=getViewNormalSelects( vUv );
// gl_FragColor=vec4(viewNormalSelects,1);return;
// if(viewNormalSelects.x<=0.&&viewNormalSelects.y<=0.&&viewNormalSelects.z<=0.) return;
float depth = getDepthSelects( vUv );
float viewZ = getViewZ( depth );
// if(-viewZ>=cameraFar) return;
float clipW = cameraProjectionMatrix[2][3] * viewZ+cameraProjectionMatrix[3][3];
vec3 viewPosition=getViewPosition( vUv, depth, clipW );
vec2 d0=gl_FragCoord.xy;
vec2 d1;
#ifdef PERSPECTIVE_CAMERA
vec3 viewIncidentDir=normalize(viewPosition);
#else
vec3 viewIncidentDir=vec3(0,0,-1);
#endif
vec3 viewRefractDir=refract(viewIncidentDir,viewNormalSelects,1./ior);
// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/refract.xhtml
vec3 d1viewPosition=viewPosition+viewRefractDir*maxDistance;
#ifdef PERSPECTIVE_CAMERA
if(d1viewPosition.z>-cameraNear){
//https://tutorial.math.lamar.edu/Classes/CalcIII/EqnsOfLines.aspx
float t=(-cameraNear-viewPosition.z)/viewRefractDir.z;
d1viewPosition=viewPosition+viewRefractDir*t;
}
#endif
d1=viewPositionToXY(d1viewPosition);
float totalLen=length(d1-d0);
float xLen=d1.x-d0.x;
float yLen=d1.y-d0.y;
float totalStep=max(abs(xLen),abs(yLen));
float xSpan=xLen/totalStep;
float ySpan=yLen/totalStep;
#ifdef FILL_HOLE
bool isRough=false;
vec2 uvRough;
#endif
for(float i=0.;i<float(MAX_STEP);i++){
if(i>=totalStep) break;
vec2 xy=vec2(d0.x+i*xSpan,d0.y+i*ySpan);
if(xy.x<0.||xy.x>resolution.x||xy.y<0.||xy.y>resolution.y) break;
float s=length(xy-d0)/totalLen;
vec2 uv=xy/resolution;
float d = getDepth(uv);
float vZ = getViewZ( d );
float cW = cameraProjectionMatrix[2][3] * vZ+cameraProjectionMatrix[3][3];
vec3 vP=getViewPosition( uv, d, cW );
#ifdef PERSPECTIVE_CAMERA
// https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
float recipVPZ=1./viewPosition.z;
float viewRefractRayZ=1./(recipVPZ+s*(1./d1viewPosition.z-recipVPZ));
float sD=surfDist*cW;
#else
float viewRefractRayZ=viewPosition.z+s*(d1viewPosition.z-viewPosition.z);
float sD=surfDist;
#endif
#ifdef FILL_HOLE // TODO: May can improve performance by check if INFINITE_THICK too.
if(viewRefractRayZ<=vZ){
if(!isRough){
uvRough=uv;
isRough=true;
}
}
#endif
bool hit;
#ifdef INFINITE_THICK
hit=viewRefractRayZ<=vZ;
#else
if(viewRefractRayZ-sD>vZ) continue;
float away=pointToLineDistance(vP,viewPosition,d1viewPosition);
hit=away<=sD;
#endif
if(hit){
setResultColor(uv);
return;
}
}
#ifdef FILL_HOLE
if(isRough){
setResultColor(uvRough);
}
// else{
// gl_FragColor=texture2D(tDiffuse,vUv);//For afterward add color mix feature.
// }
#else
// gl_FragColor=texture2D(tDiffuse,vUv);//For afterward add color mix feature.
#endif
}
`
};
var SSRrDepthShader = {
defines: {
"PERSPECTIVE_CAMERA": 1
},
uniforms: {
"tDepth": { value: null },
"cameraNear": { value: null },
"cameraFar": { value: null },
},
vertexShader: /* glsl */`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader: /* glsl */`
uniform sampler2D tDepth;
uniform float cameraNear;
uniform float cameraFar;
varying vec2 vUv;
#include <packing>
float getLinearDepth( const in vec2 uv ) {
#if PERSPECTIVE_CAMERA == 1
float fragCoordZ = texture2D( tDepth, uv ).x;
float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );
return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );
#else
return texture2D( tDepth, uv ).x;
#endif
}
void main() {
float depth = getLinearDepth( vUv );
float d = 1.0 - depth;
// d=(d-.999)*1000.;
gl_FragColor = vec4( vec3( d ), 1.0 );
}
`
};
export { SSRrShader, SSRrDepthShader };
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<title>three.js webgl - postprocessing - Screen Space Refraction</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> -
SSRrPass demo by <a href="https://github.com/gonnavis" target="_blank">Vis</a>.<br />
click object to toggle transparent<br/>
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import Stats from './jsm/libs/stats.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
import { EffectComposer } from './jsm/postprocessing/EffectComposer.js';
import { SSRrPass } from './jsm/postprocessing/SSRrPass.js';
import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
const params = {
enableSSRr: true,
autoRotate: true,
};
let composer;
let ssrrPass;
let gui;
let stats;
let controls;
let camera, scene, renderer;
const objects = [];
const selects = [];
const raycaster = new THREE.Raycaster();
const mouseDown = new THREE.Vector2();
const mouse = new THREE.Vector2();
const container = document.querySelector('#container');
// Configure and create Draco decoder.
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('js/libs/draco/');
dracoLoader.setDecoderConfig({ type: 'js' });
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 15);
camera.position.set(0.13271600513224902, 0.3489546826045913, 0.43921296427927076);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x443333);
scene.fog = new THREE.Fog(0x443333, 1, 4);
// Ground
let map=new THREE.TextureLoader().load('./textures/uv_grid_opengl.jpg')
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
map.repeat.set(20,20)
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(8, 8),
new THREE.MeshPhongMaterial({
color: 0x999999,
specular: 0x101010,
map,
})
);
plane.rotation.x = - Math.PI / 2;
plane.position.y = - 0.0001;
// plane.receiveShadow = true;
scene.add(plane);
plane.name='plane'
// Lights
const hemiLight = new THREE.HemisphereLight(0x443333, 0x111122);
hemiLight.name='hemiLight'
scene.add(hemiLight);
const spotLight = new THREE.SpotLight();
spotLight.name='spotLight'
spotLight.angle = Math.PI / 16;
spotLight.penumbra = 0.5;
// spotLight.castShadow = true;
spotLight.position.set(- 1, 1, 1);
scene.add(spotLight);
dracoLoader.load('models/draco/bunny.drc', function (geometry) {
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({ color: 0x606060 });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = - 0.0365;
mesh.name='bunny'
scene.add(mesh);
objects.push(mesh);
selects.push(mesh);
// Release decoder resources.
dracoLoader.dispose();
});
let geometry, material, mesh;
geometry = new THREE.BoxBufferGeometry(.05, .05, .05);
material = new THREE.MeshStandardMaterial({ color: 'green' });
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(- .12, .025, .015);
mesh.name='box'
scene.add(mesh);
objects.push( mesh );
selects.push( mesh );
geometry = new THREE.IcosahedronBufferGeometry(.025, 4);
material = new THREE.MeshStandardMaterial({ color: 'cyan' });
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(- .05, .025, .08);
mesh.name='sphere'
scene.add(mesh);
objects.push( mesh );
// selects.push( mesh );
geometry = new THREE.ConeBufferGeometry(.025, .05, 64);
material = new THREE.MeshStandardMaterial({ color: 'yellow' });
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(- .05, .025, - .055);
mesh.name='cone'
scene.add(mesh);
objects.push( mesh );
// selects.push( mesh );
// renderer
renderer = new THREE.WebGLRenderer({ antialias: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.autoClear=false
container.appendChild(renderer.domElement);
//
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 0.0635, 0);
controls.update();
controls.enabled = !params.autoRotate;
// STATS
stats = new Stats();
container.appendChild(stats.dom);
window.addEventListener('resize', onWindowResize, false);
window.addEventListener( 'pointerdown', onPointerDown, false );
window.addEventListener( 'pointerup', onPointerUp, false );
// composer
composer = new EffectComposer(renderer);
ssrrPass = new SSRrPass({
renderer,
scene,
camera,
width: innerWidth,
height: innerHeight,
encoding: THREE.sRGBEncoding,
selects: selects
});
composer.addPass(ssrrPass);
// GUI
gui = new GUI();
gui.add(params, 'enableSSRr').name('Enable SSRr');
ssrrPass.ior = 1.1;
gui.add(ssrrPass, 'ior').name('IOR').min(1).max(1.5).step(.0001);
gui.add( ssrrPass, 'fillHole' );
gui.add(params, 'autoRotate').onChange(() => {
controls.enabled = !params.autoRotate;
});
const folder = gui.addFolder('more settings');
folder.add( ssrrPass, 'specular' );
folder.add(ssrrPass.specularMaterial, 'metalness').min(0).max(1).step(.01);
folder.add(ssrrPass.specularMaterial, 'roughness').min(0).max(1).step(.01);
folder.add(ssrrPass, 'output', {
'Default': SSRrPass.OUTPUT.Default,
'SSRr Only': SSRrPass.OUTPUT.SSRr,
'Beauty': SSRrPass.OUTPUT.Beauty,
'Depth': SSRrPass.OUTPUT.Depth,
'DepthSelects': SSRrPass.OUTPUT.DepthSelects,
'NormalSelects': SSRrPass.OUTPUT.NormalSelects,
'Refractive': SSRrPass.OUTPUT.Refractive,
'Specular': SSRrPass.OUTPUT.Specular,
}).onChange(function (value) {
ssrrPass.output = parseInt(value);
});
ssrrPass.surfDist = 0.0015;
folder.add( ssrrPass, 'surfDist' ).min( 0 ).max( .005 ).step( .0001 );
ssrrPass.maxDistance = 50;
folder.add( ssrrPass, 'maxDistance' ).min( 0 ).max( 100 ).step( .001 )
folder.add( ssrrPass, 'infiniteThick' );
// folder.open()
// gui.close()
}
function onPointerDown( event ) {
mouseDown.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouseDown.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onPointerUp( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
if(mouseDown.sub(mouse).length()>0) return
raycaster.setFromCamera( mouse, camera );
const intersect = raycaster.intersectObjects( objects )[0];
if(intersect){
let index=selects.indexOf(intersect.object)
if(index>=0){
selects.splice(index,1)
}else{
selects.push(intersect.object)
}
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
stats.begin();
render();
stats.end();
}
function render() {
if (params.autoRotate) {
const timer = Date.now() * 0.0003;
camera.position.x = Math.sin(timer) * 0.5;
camera.position.y = 0.2135;
camera.position.z = Math.cos(timer) * 0.5;
camera.lookAt(0, 0.0635, 0);
} else {
controls.update();
}
if (params.enableSSRr) {
composer.render();
} else {
renderer.render(scene, camera);
}
}
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册