Last active
July 31, 2024 22:13
-
-
Save Codezigineer/354e01e724f73e87f6ceef8be65f498e to your computer and use it in GitHub Desktop.
Realistic fork of Screen Space Reflections implementation from Threejs Examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Matrix4, | |
Vector2, | |
AddEquation, | |
Color, | |
NormalBlending, | |
DepthTexture, | |
SrcAlphaFactor, | |
OneMinusSrcAlphaFactor, | |
MeshNormalMaterial, | |
MeshBasicMaterial, | |
NearestFilter, | |
NoBlending, | |
ShaderMaterial, | |
UniformsUtils, | |
UnsignedShortType, | |
WebGLRenderTarget, | |
HalfFloatType, | |
} from 'three'; | |
import { Pass, FullScreenQuad } from './Pass.js'; | |
import { CopyShader } from '../shaders/CopyShader.js'; | |
/** | |
* References: | |
* https://lettier.github.io/3d-game-shaders-for-beginners/screen-space-reflection.html | |
* https://www.shadertoy.com/view/XfVGD1 | |
*/ | |
const SSRShader = { | |
name: 'SSRShader', | |
defines: { | |
MAX_STEP: 0, | |
PERSPECTIVE_CAMERA: true, | |
DISTANCE_ATTENUATION: true, | |
FRESNEL: true, | |
INFINITE_THICK: false, | |
SELECTIVE: false, | |
}, | |
uniforms: { | |
'tDiffuse': { value: null }, | |
'tNormal': { value: null }, | |
'tMetalness': { value: null }, | |
'tDepth': { value: null }, | |
'tAlbedo': { value: null }, | |
'tRoughness': { value: null }, | |
'cameraNear': { value: null }, | |
'cameraFar': { value: null }, | |
'resolution': { value: new Vector2() }, | |
'cameraProjectionMatrix': { value: new Matrix4() }, | |
'cameraInverseProjectionMatrix': { value: new Matrix4() }, | |
'opacity': { value: 1 }, | |
'maxDistance': { value: 180 }, | |
'cameraRange': { value: 0 }, | |
'thickness': { value: .018 } | |
}, | |
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 tNormal; | |
uniform sampler2D tMetalness; | |
uniform sampler2D tDiffuse; | |
uniform sampler2D tRoughness; | |
uniform sampler2D tAlbedo; | |
uniform float cameraRange; | |
uniform vec2 resolution; | |
uniform float opacity; | |
uniform float cameraNear; | |
uniform float cameraFar; | |
uniform float maxDistance; | |
uniform float thickness; | |
uniform mat4 cameraProjectionMatrix; | |
uniform mat4 cameraInverseProjectionMatrix; | |
#include <packing> | |
// from https://www.shadertoy.com/view/XfVGD1 | |
vec4 sampleBlur(in sampler2D iSampler, in vec2 uv, in float roughness) | |
{ | |
float blur = roughness * 6.0; | |
const float s = 0.5; | |
// weigh each lod based on the blur amount (0-4) | |
vec4 w = vec4(0.0, 1.0, 2.0, 3.0) * s - blur * s; | |
w = exp(-(w*w)); | |
w *= 1.0 / (w.x + w.y + w.z + w.w); | |
vec4 lodBase = vec4(0.0, 1.0, 2.0, 3.0) + blur; | |
vec4 lod0 = textureLod(iSampler, uv, lodBase.x); | |
vec4 lod1 = textureLod(iSampler, uv, lodBase.y); | |
vec4 lod2 = textureLod(iSampler, uv, lodBase.z); | |
vec4 lod3 = textureLod(iSampler, uv, lodBase.w); | |
return lod0 * w.x | |
+ lod1 * w.y | |
+ lod2 * w.z | |
+ lod3 * w.w; | |
} | |
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 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 getViewNormal( const in vec2 uv ) { | |
return unpackRGBToNormal( texture2D( tNormal, 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 main(){ | |
float metalness=texture2D(tMetalness,vUv).r; | |
float depth = getDepth( 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; | |
vec3 viewNormal=getViewNormal( vUv ); | |
#ifdef PERSPECTIVE_CAMERA | |
vec3 viewIncidentDir=normalize(viewPosition); | |
vec3 viewReflectDir=reflect(viewIncidentDir,viewNormal); | |
#else | |
vec3 viewIncidentDir=vec3(0,0,-1); | |
vec3 viewReflectDir=reflect(viewIncidentDir,viewNormal); | |
#endif | |
float maxReflectRayLen=maxDistance/dot(-viewIncidentDir,viewNormal); | |
// dot(a,b)==length(a)*length(b)*cos(theta) // https://www.mathsisfun.com/algebra/vectors-dot-product.html | |
// if(a.isNormalized&&b.isNormalized) dot(a,b)==cos(theta) | |
// maxDistance/maxReflectRayLen=cos(theta) | |
// maxDistance/maxReflectRayLen==dot(a,b) | |
// maxReflectRayLen==maxDistance/dot(a,b) | |
vec3 d1viewPosition=viewPosition+viewReflectDir*maxReflectRayLen; | |
#ifdef PERSPECTIVE_CAMERA | |
if(d1viewPosition.z>-cameraNear){ | |
//https://tutorial.math.lamar.edu/Classes/CalcIII/EqnsOfLines.aspx | |
float t=(-cameraNear-viewPosition.z)/viewReflectDir.z; | |
d1viewPosition=viewPosition+viewReflectDir*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; | |
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 ); | |
if(-vZ>=cameraFar) continue; | |
float cW = cameraProjectionMatrix[2][3] * vZ+cameraProjectionMatrix[3][3]; | |
vec3 vP=getViewPosition( uv, d, cW ); | |
#ifdef PERSPECTIVE_CAMERA | |
// https://comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf | |
float recipVPZ=1./viewPosition.z; | |
float viewReflectRayZ=1./(recipVPZ+s*(1./d1viewPosition.z-recipVPZ)); | |
#else | |
float viewReflectRayZ=viewPosition.z+s*(d1viewPosition.z-viewPosition.z); | |
#endif | |
// if(viewReflectRayZ>vZ) continue; // will cause "npm run make-screenshot webgl_postprocessing_ssr" high probability hang. | |
// https://github.com/mrdoob/three.js/pull/21539#issuecomment-821061164 | |
if(viewReflectRayZ<=vZ){ | |
bool hit; | |
#ifdef INFINITE_THICK | |
hit=true; | |
#else | |
float away=pointToLineDistance(vP,viewPosition,d1viewPosition); | |
float minThickness; | |
vec2 xyNeighbor=xy; | |
xyNeighbor.x+=1.; | |
vec2 uvNeighbor=xyNeighbor/resolution; | |
vec3 vPNeighbor=getViewPosition(uvNeighbor,d,cW); | |
minThickness=vPNeighbor.x-vP.x; | |
minThickness*=3.; | |
float tk=max(minThickness,thickness); | |
hit=away<=tk; | |
#endif | |
if(hit){ | |
vec3 vN=getViewNormal( uv ); | |
if(dot(viewReflectDir,vN)>=0.) continue; | |
float distance=pointPlaneDistance(vP,viewPosition,viewNormal); | |
float op=opacity; | |
#ifdef FRESNEL | |
float fresnelCoe=(dot(viewIncidentDir,viewReflectDir)+1.)/2.; | |
op*=fresnelCoe; | |
#endif | |
float roughness = texture2D(tRoughness,vUv).r; | |
vec4 reflectColor = sampleBlur(tDiffuse, uv, roughness) * op; | |
vec4 noMetalic = texture2D(tDiffuse, uv) + reflectColor; | |
vec4 fullMetallic = reflectColor * texture2D(tAlbedo, vUv); | |
gl_FragColor.xyz=mix(noMetallic, fullMetallic, metalness).xyz; | |
gl_FragColor.a=1.0; | |
break; | |
} | |
} | |
} | |
} | |
` | |
}; | |
const SSRDepthShader = { | |
name: 'SSRDepthShader', | |
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 ); | |
} | |
` | |
}; | |
class SSRPass extends Pass { | |
constructor( { renderer, scene, camera, width, height, selects, bouncing = false, groundReflector } ) { | |
super(); | |
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.groundReflector = groundReflector; | |
this.opacity = 1.0; | |
this.output = 0; | |
this.maxDistance = SSRShader.uniforms.maxDistance.value; | |
this.thickness = SSRShader.uniforms.thickness.value; | |
this.tempColor = new Color(); | |
this._selects = selects; | |
this.selective = Array.isArray( this._selects ); | |
this._bouncing = bouncing; | |
Object.defineProperty( this, 'bouncing', { | |
get() { | |
return this._bouncing; | |
}, | |
set( val ) { | |
if ( this._bouncing === val ) return; | |
this._bouncing = val; | |
if ( val ) { | |
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.prevRenderTarget.texture; | |
} else { | |
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; | |
} | |
} | |
} ); | |
this.blur = true; | |
this._distanceAttenuation = SSRShader.defines.DISTANCE_ATTENUATION; | |
Object.defineProperty( this, 'distanceAttenuation', { | |
get() { | |
return this._distanceAttenuation; | |
}, | |
set( val ) { | |
if ( this._distanceAttenuation === val ) return; | |
this._distanceAttenuation = val; | |
this.ssrMaterial.defines.DISTANCE_ATTENUATION = val; | |
this.ssrMaterial.needsUpdate = true; | |
} | |
} ); | |
this._fresnel = SSRShader.defines.FRESNEL; | |
Object.defineProperty( this, 'fresnel', { | |
get() { | |
return this._fresnel; | |
}, | |
set( val ) { | |
if ( this._fresnel === val ) return; | |
this._fresnel = val; | |
this.ssrMaterial.defines.FRESNEL = val; | |
this.ssrMaterial.needsUpdate = true; | |
} | |
} ); | |
this._infiniteThick = SSRShader.defines.INFINITE_THICK; | |
Object.defineProperty( this, 'infiniteThick', { | |
get() { | |
return this._infiniteThick; | |
}, | |
set( val ) { | |
if ( this._infiniteThick === val ) return; | |
this._infiniteThick = val; | |
this.ssrMaterial.defines.INFINITE_THICK = val; | |
this.ssrMaterial.needsUpdate = true; | |
} | |
} ); | |
// beauty render target with depth buffer | |
const depthTexture = new DepthTexture(); | |
depthTexture.type = UnsignedShortType; | |
depthTexture.minFilter = NearestFilter; | |
depthTexture.magFilter = NearestFilter; | |
this.beautyRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter, | |
type: HalfFloatType, | |
depthTexture: depthTexture, | |
depthBuffer: true | |
} ); | |
//for bouncing | |
this.prevRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter | |
} ); | |
// normal render target | |
this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter, | |
type: HalfFloatType, | |
} ); | |
this.roughnessRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter, | |
type: HalfFloatType, | |
} ); | |
this.albedoRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter, | |
} ); | |
// metalness render target | |
this.metalnessRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter, | |
type: HalfFloatType, | |
} ); | |
// ssr render target | |
this.ssrRenderTarget = new WebGLRenderTarget( this.width, this.height, { | |
minFilter: NearestFilter, | |
magFilter: NearestFilter | |
} ); | |
// this.blurRenderTarget3 = this.ssrRenderTarget.clone(); | |
// ssr material | |
this.ssrMaterial = new ShaderMaterial( { | |
defines: Object.assign( {}, SSRShader.defines, { | |
MAX_STEP: Math.sqrt( this.width * this.width + this.height * this.height ) | |
} ), | |
uniforms: UniformsUtils.clone( SSRShader.uniforms ), | |
vertexShader: SSRShader.vertexShader, | |
fragmentShader: SSRShader.fragmentShader, | |
blending: NoBlending | |
} ); | |
this.ssrMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; | |
this.ssrMaterial.uniforms[ 'tNormal' ].value = this.normalRenderTarget.texture; | |
this.ssrMaterial.defines.SELECTIVE = this.selective; | |
this.ssrMaterial.needsUpdate = true; | |
this.ssrMaterial.uniforms[ 'tMetalness' ].value = this.metalnessRenderTarget.texture; | |
this.ssrMaterial.uniforms[ 'tRoughness' ].value = this.roughnessRenderTarget.texture; | |
this.ssrMaterial.uniforms[ 'tAlbedo' ].value = this.albedoRenderTarget.texture; | |
this.ssrMaterial.uniforms[ 'tDepth' ].value = this.beautyRenderTarget.depthTexture; | |
this.ssrMaterial.uniforms[ 'cameraNear' ].value = this.camera.near; | |
this.ssrMaterial.uniforms[ 'cameraFar' ].value = this.camera.far; | |
this.ssrMaterial.uniforms[ 'thickness' ].value = this.thickness; | |
this.ssrMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height ); | |
this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); | |
this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse ); | |
// normal material | |
this.normalMaterial = new MeshNormalMaterial(); | |
this.normalMaterial.blending = NoBlending; | |
// metalnessOn material | |
this.metalnessOnMaterial = new MeshBasicMaterial( { | |
color: 'white' | |
} ); | |
// metalnessOff material | |
this.metalnessOffMaterial = new MeshBasicMaterial( { | |
color: 'black' | |
} ); | |
// blur material | |
// blur material 2 | |
// // blur material 3 | |
// this.blurMaterial3 = new ShaderMaterial({ | |
// defines: Object.assign({}, SSRBlurShader.defines), | |
// uniforms: UniformsUtils.clone(SSRBlurShader.uniforms), | |
// vertexShader: SSRBlurShader.vertexShader, | |
// fragmentShader: SSRBlurShader.fragmentShader | |
// }); | |
// this.blurMaterial3.uniforms['tDiffuse'].value = this.blurRenderTarget2.texture; | |
// this.blurMaterial3.uniforms['resolution'].value.set(this.width, this.height); | |
// material for rendering the depth | |
this.depthRenderMaterial = new ShaderMaterial( { | |
defines: Object.assign( {}, SSRDepthShader.defines ), | |
uniforms: UniformsUtils.clone( SSRDepthShader.uniforms ), | |
vertexShader: SSRDepthShader.vertexShader, | |
fragmentShader: SSRDepthShader.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 FullScreenQuad( null ); | |
this.originalClearColor = new Color(); | |
} | |
dispose() { | |
// dispose render targets | |
this.beautyRenderTarget.dispose(); | |
this.prevRenderTarget.dispose(); | |
this.normalRenderTarget.dispose(); | |
this.metalnessRenderTarget.dispose(); | |
this.roughnessRenderTarget.dispose(); | |
this.albedoRenderTarget.dispose(); | |
this.ssrRenderTarget.dispose(); | |
// this.blurRenderTarget3.dispose(); | |
// dispose materials | |
this.normalMaterial.dispose(); | |
this.metalnessOnMaterial.dispose(); | |
this.metalnessOffMaterial.dispose(); | |
this.copyMaterial.dispose(); | |
this.depthRenderMaterial.dispose(); | |
// dipsose full screen quad | |
this.fsQuad.dispose(); | |
} | |
render( renderer, writeBuffer /*, readBuffer, deltaTime, maskActive */ ) { | |
// render beauty and depth | |
renderer.setRenderTarget( this.beautyRenderTarget ); | |
renderer.clear(); | |
if ( this.groundReflector ) { | |
this.groundReflector.visible = false; | |
this.groundReflector.doRender( this.renderer, this.scene, this.camera ); | |
this.groundReflector.visible = true; | |
} | |
renderer.render( this.scene, this.camera ); | |
if ( this.groundReflector ) this.groundReflector.visible = false; | |
// render normals | |
this.renderOverride( renderer, this.normalMaterial, this.normalRenderTarget, 0, 0 ); | |
// render metalnesses | |
if ( this.selective ) { | |
this.renderMetalness( renderer, this.metalnessOnMaterial, this.metalnessRenderTarget, 0, 0 ); | |
} | |
// render SSR | |
this.ssrMaterial.uniforms[ 'opacity' ].value = this.opacity; | |
this.ssrMaterial.uniforms[ 'maxDistance' ].value = this.maxDistance; | |
this.ssrMaterial.uniforms[ 'thickness' ].value = this.thickness; | |
this.renderPass( renderer, this.ssrMaterial, this.ssrRenderTarget ); | |
// output result to screen | |
switch ( this.output ) { | |
case SSRPass.OUTPUT.Default: | |
case SSRPass.OUTPUT.SSR: | |
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture; | |
this.copyMaterial.blending = NoBlending; | |
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); | |
if ( this.bouncing ) { | |
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture; | |
this.copyMaterial.blending = NoBlending; | |
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget ); | |
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssrRenderTarget.texture; | |
this.copyMaterial.blending = NormalBlending; | |
this.renderPass( renderer, this.copyMaterial, this.prevRenderTarget ); | |
} | |
break; | |
case SSRPass.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 SSRPass.OUTPUT.Depth: | |
this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer ); | |
break; | |
case SSRPass.OUTPUT.Normal: | |
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture; | |
this.copyMaterial.blending = NoBlending; | |
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); | |
break; | |
case SSRPass.OUTPUT.Metalness: | |
this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.metalnessRenderTarget.texture; | |
this.copyMaterial.blending = NoBlending; | |
this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer ); | |
break; | |
default: | |
console.warn( 'THREE.SSRPass: Unknown output type.' ); | |
} | |
} | |
renderPass( renderer, passMaterial, renderTarget, clearColor, clearAlpha ) { | |
// save original state | |
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) ); | |
const originalClearAlpha = renderer.getClearAlpha( this.tempColor ); | |
const 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( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { | |
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) ); | |
const originalClearAlpha = renderer.getClearAlpha( this.tempColor ); | |
const 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 ); | |
} | |
renderMetalness( renderer, overrideMaterial, renderTarget, clearColor, clearAlpha ) { | |
this.originalClearColor.copy( renderer.getClearColor( this.tempColor ) ); | |
const originalClearAlpha = renderer.getClearAlpha( this.tempColor ); | |
const 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.traverseVisible( child => { | |
child._SSRPassBackupMaterial = child.material; | |
if ( this._selects.includes( child ) ) { | |
child.material = this.metalnessOnMaterial; | |
} else { | |
child.material = this.metalnessOffMaterial; | |
} | |
} ); | |
renderer.render( this.scene, this.camera ); | |
this.scene.traverseVisible( child => { | |
child.material = child._SSRPassBackupMaterial; | |
} ); | |
// restore original state | |
renderer.autoClear = originalAutoClear; | |
renderer.setClearColor( this.originalClearColor ); | |
renderer.setClearAlpha( originalClearAlpha ); | |
} | |
setSize( width, height ) { | |
this.width = width; | |
this.height = height; | |
this.ssrMaterial.defines.MAX_STEP = Math.sqrt( width * width + height * height ); | |
this.ssrMaterial.needsUpdate = true; | |
this.beautyRenderTarget.setSize( width, height ); | |
this.prevRenderTarget.setSize( width, height ); | |
this.ssrRenderTarget.setSize( width, height ); | |
this.normalRenderTarget.setSize( width, height ); | |
this.metalnessRenderTarget.setSize( width, height ); | |
this.blurRenderTarget.setSize( width, height ); | |
this.blurRenderTarget2.setSize( width, height ); | |
// this.blurRenderTarget3.setSize(width, height); | |
this.ssrMaterial.uniforms[ 'resolution' ].value.set( width, height ); | |
this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix ); | |
this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse ); | |
this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height ); | |
this.blurMaterial2.uniforms[ 'resolution' ].value.set( width, height ); | |
} | |
} | |
SSRPass.OUTPUT = { | |
'Default': 0, | |
'SSR': 1, | |
'Beauty': 3, | |
'Depth': 4, | |
'Normal': 5, | |
'Metalness': 7, | |
}; | |
export { SSRPass }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment