теперь я хочу реализовать функцию SSAO без композитора
инициализировать мой ssaoRender
Код: Выделить всё
RenderView.ssaoRenderer = new SSAORenderer(this._renderer as THREE.WebGLRenderer, this._renderEngine.mainScene, camera );
Код: Выделить всё
if (RenderView.ssaoRenderer?.enabled) {
RenderView.ssaoRenderer.render();
} else {
renderer.render(this, camera);
}
Код: Выделить всё
export const ssaoVertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
`;
export const ssaoFragmentShader = `
varying vec2 vUv;
uniform sampler2D depthMap;
uniform sampler2D rotateMap;
uniform float zNear;
uniform float zFar;
uniform float radius;
uniform float attBias;
uniform float attScale;
vec3 rndTable[8];
void initRndTable() {
rndTable[0] = vec3(-0.5, -0.5, -0.5);
rndTable[1] = vec3(0.5, -0.5, -0.5);
rndTable[2] = vec3(-0.5, 0.5, -0.5);
rndTable[3] = vec3(0.5, 0.5, -0.5);
rndTable[4] = vec3(-0.5, -0.5, 0.5);
rndTable[5] = vec3(0.5, -0.5, 0.5);
rndTable[6] = vec3(-0.5, 0.5, 0.5);
rndTable[7] = vec3(0.5, 0.5, 0.5);
}
float linearizeDepth(float z) {
float ndc = z * 2.0 - 1.0;
return 2.0 * zNear * zFar / (zFar + zNear - ndc * (zFar - zNear));
}
void main() {
initRndTable();
float zb = texture2D(depthMap, vUv).r;
float z = linearizeDepth(zb);
vec3 plane = 2.0 * texture2D(rotateMap, vUv).xyz - 1.0;
float att = 0.0;
for (int i = 0; i < 8; i++) {
vec3 sampleVec = reflect(rndTable[i], plane);
vec2 offsetUv = vUv + radius * sampleVec.xy / z;
if (offsetUv.x < 0.0 || offsetUv.x > 1.0 || offsetUv.y < 0.0 || offsetUv.y > 1.0) continue;
float zSample = texture2D(depthMap, offsetUv).r;
zSample = linearizeDepth(zSample);
if (zSample - z > 0.1) continue;
float dz = max(zSample - z, 0.0) * 30.0;
att += 1.0 / (1.0 + dz * dz);
}
att = clamp((att / 8.0 + attBias) * attScale, 0.0, 1.0);
gl_FragColor = vec4(vec3(att), 1.0);
}
`;
Код: Выделить всё
ssaoRenderer.ts
import { THREE } from '@render/three';
import { DepthPass } from './depthPass';
import { ssaoFragmentShader, ssaoVertexShader } from '@render/utils/ssaoPass';
export class SSAORenderer {
private renderer: THREE.WebGLRenderer;
private scene: THREE.Scene;
private camera: THREE.Camera;
private depthPass: DepthPass;
private ssaoMaterial: THREE.ShaderMaterial;
private quadScene: THREE.Scene;
private quadCamera: THREE.OrthographicCamera;
enabled = true;
constructor(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera){
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
const width = renderer.domElement.width;
const height = renderer.domElement.height;
this.depthPass = new DepthPass(width, height);
this.ssaoMaterial = new THREE.ShaderMaterial({
vertexShader: ssaoVertexShader,
fragmentShader: ssaoFragmentShader,
uniforms: {
depthMap: { value: null },
rotateMap: { value: null },
zNear: { value: (camera as THREE.PerspectiveCamera).near },
zFar: { value: (camera as THREE.PerspectiveCamera).far },
radius: { value: 0.2 },
attBias: { value: 0.3 },
attScale: { value: 1.0 }
}
});
const quad = new THREE.Mesh(new THREE.PlaneGeometry(2,2), this.ssaoMaterial);
this.quadScene = new THREE.Scene();
this.quadScene.add(quad);
this.quadCamera = new THREE.OrthographicCamera(-1,1,1,-1,0,1);
this.ssaoMaterial.uniforms.projectionMatrix = { value: camera.projectionMatrix };
this.ssaoMaterial.uniforms.invProjectionMatrix = { value: camera.projectionMatrixInverse };
}
public render(){
if(!this.enabled){
this.renderer.render(this.scene, this.camera);
return;
}
this.ssaoMaterial.uniforms.projectionMatrix.value = this.camera.projectionMatrix;
this.ssaoMaterial.uniforms.invProjectionMatrix.value = this.camera.projectionMatrixInverse;
const width = this.renderer.domElement.width;
const height = this.renderer.domElement.height;
this.depthPass.render(this.renderer, this.scene, this.camera);
this.ssaoMaterial.uniforms.tDepth.value = this.depthPass.getDepthTexture();
this.ssaoMaterial.uniforms.tColor.value = this.depthPass.getColorTexture();
this.ssaoMaterial.uniforms.tNormal.value = this.depthPass.getNormalTexture();
this.ssaoMaterial.uniforms.resolution.value.set(width,height);
this.ssaoMaterial.uniforms.projectionMatrix.value = this.camera.projectionMatrix;
this.ssaoMaterial.uniforms.invProjectionMatrix.value = this.camera.projectionMatrixInverse;
this.renderer.render(this.quadScene, this.quadCamera);
}
public resize(width:number, height:number){
this.depthPass.resize(width,height);
this.ssaoMaterial.uniforms.resolution.value.set(width,height);
}
public setIntensity(v:number){
this.ssaoMaterial.uniforms.intensity.value = v;
}
public setRadius(v:number){
this.ssaoMaterial.uniforms.radius.value = v;
}
}
Код: Выделить всё
// depthPass.ts
import { THREE } from '@render/three';
export class DepthPass {
public renderTarget: THREE.WebGLRenderTarget;
public normalTarget: THREE.WebGLRenderTarget;
private normalMaterial: THREE.ShaderMaterial;
constructor(width: number, height: number) {
this.renderTarget = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBAFormat
});
this.renderTarget.depthTexture = new THREE.DepthTexture(width, height);
this.renderTarget.depthTexture.format = THREE.DepthFormat;
this.renderTarget.depthTexture.type = THREE.FloatType;
this.renderTarget.depthBuffer = true;
this.normalTarget = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.NearestFilter,
magFilter: THREE.NearestFilter,
format: THREE.RGBAFormat
});
this.normalMaterial = new THREE.ShaderMaterial({
vertexShader: `
varying vec3 vNormalView;
void main() {
vNormalView = normalize((viewMatrix * vec4(normal,0.0)).xyz);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`,
fragmentShader: `
varying vec3 vNormalView;
void main() {
gl_FragColor = vec4(vNormalView * 0.5 + 0.5, 1.0);
}
`
});
}
public render2(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) {
// depth only
const depthMaterial = new THREE.MeshDepthMaterial();
scene.overrideMaterial = depthMaterial;
renderer.setRenderTarget(this.renderTarget);
renderer.clear();
renderer.render(scene, camera);
scene.overrideMaterial = null;
renderer.setRenderTarget(null);
}
public render(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) {
const originalMaterials = new Map();
scene.traverse(obj => {
if (obj instanceof THREE.Mesh && obj.material) {
originalMaterials.set(obj, obj.material);
}
});
renderer.setRenderTarget(this.renderTarget);
renderer.clear(true, true, true);
renderer.render(scene, camera);
scene.traverse(obj => {
if (obj instanceof THREE.Mesh) obj.material = this.normalMaterial;
});
renderer.setRenderTarget(this.normalTarget);
renderer.clear(true, true, true);
renderer.render(scene, camera);
scene.traverse(obj => {
if (obj instanceof THREE.Mesh && originalMaterials.has(obj)) {
obj.material = originalMaterials.get(obj)!;
}
});
renderer.setRenderTarget(null);
}
public resize(width: number, height: number) {
this.renderTarget.setSize(width, height);
this.normalTarget.setSize(width, height);
}
public getDepthTexture(): THREE.DepthTexture {
return this.renderTarget.depthTexture;
}
public getColorTexture(): THREE.Texture {
return this.renderTarget.texture;
}
public getNormalTexture(): THREE.Texture {
return this.normalTarget.texture;
}
}
Шейдер применен неправильно; он слегка затемняет всю модель, и нет эффекта SSAO.
Я активирую ssao с помощью флажка, активация вока - это хорошо
Подробнее здесь: https://stackoverflow.com/questions/798 ... h-three-js