197 lines
6.5 KiB
JavaScript
197 lines
6.5 KiB
JavaScript
|
|
// NormalPass will generate normal and depth data.
|
||
|
|
// TODO Animation
|
||
|
|
import Texture2D from 'claygl/src/Texture2D';
|
||
|
|
import Texture from 'claygl/src/Texture';
|
||
|
|
import Shader from 'claygl/src/Shader';
|
||
|
|
import FrameBuffer from 'claygl/src/FrameBuffer';
|
||
|
|
import Material from 'claygl/src/Material';
|
||
|
|
import Pass from 'claygl/src/compositor/Pass';
|
||
|
|
import textureUtil from 'claygl/src/util/texture';
|
||
|
|
import normalGLSL from '../util/shader/normal.glsl.js';
|
||
|
|
Shader.import(normalGLSL);
|
||
|
|
|
||
|
|
function attachTextureToSlot(renderer, program, symbol, texture, slot) {
|
||
|
|
var gl = renderer.gl;
|
||
|
|
program.setUniform(gl, '1i', symbol, slot);
|
||
|
|
gl.activeTexture(gl.TEXTURE0 + slot); // Maybe texture is not loaded yet;
|
||
|
|
|
||
|
|
if (texture.isRenderable()) {
|
||
|
|
texture.bind(renderer);
|
||
|
|
} else {
|
||
|
|
// Bind texture to null
|
||
|
|
texture.unbind(renderer);
|
||
|
|
}
|
||
|
|
} // TODO Use globalShader insteadof globalMaterial?
|
||
|
|
|
||
|
|
|
||
|
|
function getBeforeRenderHook(renderer, defaultNormalMap, defaultBumpMap, defaultRoughnessMap, normalMaterial) {
|
||
|
|
var previousNormalMap;
|
||
|
|
var previousBumpMap;
|
||
|
|
var previousRoughnessMap;
|
||
|
|
var previousRenderable;
|
||
|
|
var gl = renderer.gl;
|
||
|
|
return function (renderable, normalMaterial, prevNormalMaterial) {
|
||
|
|
// Material not change
|
||
|
|
if (previousRenderable && previousRenderable.material === renderable.material) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var material = renderable.material;
|
||
|
|
var program = renderable.__program;
|
||
|
|
var roughness = material.get('roughness');
|
||
|
|
|
||
|
|
if (roughness == null) {
|
||
|
|
roughness = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
var normalMap = material.get('normalMap') || defaultNormalMap;
|
||
|
|
var roughnessMap = material.get('roughnessMap');
|
||
|
|
var bumpMap = material.get('bumpMap');
|
||
|
|
var uvRepeat = material.get('uvRepeat');
|
||
|
|
var uvOffset = material.get('uvOffset');
|
||
|
|
var detailUvRepeat = material.get('detailUvRepeat');
|
||
|
|
var detailUvOffset = material.get('detailUvOffset');
|
||
|
|
var useBumpMap = !!bumpMap && material.isTextureEnabled('bumpMap');
|
||
|
|
var useRoughnessMap = !!roughnessMap && material.isTextureEnabled('roughnessMap');
|
||
|
|
var doubleSide = material.isDefined('fragment', 'DOUBLE_SIDED');
|
||
|
|
bumpMap = bumpMap || defaultBumpMap;
|
||
|
|
roughnessMap = roughnessMap || defaultRoughnessMap;
|
||
|
|
|
||
|
|
if (prevNormalMaterial !== normalMaterial) {
|
||
|
|
normalMaterial.set('normalMap', normalMap);
|
||
|
|
normalMaterial.set('bumpMap', bumpMap);
|
||
|
|
normalMaterial.set('roughnessMap', roughnessMap);
|
||
|
|
normalMaterial.set('useBumpMap', useBumpMap);
|
||
|
|
normalMaterial.set('useRoughnessMap', useRoughnessMap);
|
||
|
|
normalMaterial.set('doubleSide', doubleSide);
|
||
|
|
uvRepeat != null && normalMaterial.set('uvRepeat', uvRepeat);
|
||
|
|
uvOffset != null && normalMaterial.set('uvOffset', uvOffset);
|
||
|
|
detailUvRepeat != null && normalMaterial.set('detailUvRepeat', detailUvRepeat);
|
||
|
|
detailUvOffset != null && normalMaterial.set('detailUvOffset', detailUvOffset);
|
||
|
|
normalMaterial.set('roughness', roughness);
|
||
|
|
} else {
|
||
|
|
program.setUniform(gl, '1f', 'roughness', roughness);
|
||
|
|
|
||
|
|
if (previousNormalMap !== normalMap) {
|
||
|
|
attachTextureToSlot(renderer, program, 'normalMap', normalMap, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (previousBumpMap !== bumpMap && bumpMap) {
|
||
|
|
attachTextureToSlot(renderer, program, 'bumpMap', bumpMap, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (previousRoughnessMap !== roughnessMap && roughnessMap) {
|
||
|
|
attachTextureToSlot(renderer, program, 'roughnessMap', roughnessMap, 2);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (uvRepeat != null) {
|
||
|
|
program.setUniform(gl, '2f', 'uvRepeat', uvRepeat);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (uvOffset != null) {
|
||
|
|
program.setUniform(gl, '2f', 'uvOffset', uvOffset);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (detailUvRepeat != null) {
|
||
|
|
program.setUniform(gl, '2f', 'detailUvRepeat', detailUvRepeat);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (detailUvOffset != null) {
|
||
|
|
program.setUniform(gl, '2f', 'detailUvOffset', detailUvOffset);
|
||
|
|
}
|
||
|
|
|
||
|
|
program.setUniform(gl, '1i', 'useBumpMap', +useBumpMap);
|
||
|
|
program.setUniform(gl, '1i', 'useRoughnessMap', +useRoughnessMap);
|
||
|
|
program.setUniform(gl, '1i', 'doubleSide', +doubleSide);
|
||
|
|
}
|
||
|
|
|
||
|
|
previousNormalMap = normalMap;
|
||
|
|
previousBumpMap = bumpMap;
|
||
|
|
previousRoughnessMap = roughnessMap;
|
||
|
|
previousRenderable = renderable;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function NormalPass(opt) {
|
||
|
|
opt = opt || {};
|
||
|
|
this._depthTex = new Texture2D({
|
||
|
|
format: Texture.DEPTH_COMPONENT,
|
||
|
|
type: Texture.UNSIGNED_INT
|
||
|
|
});
|
||
|
|
this._normalTex = new Texture2D({
|
||
|
|
type: Texture.HALF_FLOAT
|
||
|
|
});
|
||
|
|
this._framebuffer = new FrameBuffer();
|
||
|
|
|
||
|
|
this._framebuffer.attach(this._normalTex);
|
||
|
|
|
||
|
|
this._framebuffer.attach(this._depthTex, FrameBuffer.DEPTH_ATTACHMENT);
|
||
|
|
|
||
|
|
this._normalMaterial = new Material({
|
||
|
|
shader: new Shader(Shader.source('ecgl.normal.vertex'), Shader.source('ecgl.normal.fragment'))
|
||
|
|
});
|
||
|
|
|
||
|
|
this._normalMaterial.enableTexture(['normalMap', 'bumpMap', 'roughnessMap']);
|
||
|
|
|
||
|
|
this._defaultNormalMap = textureUtil.createBlank('#000');
|
||
|
|
this._defaultBumpMap = textureUtil.createBlank('#000');
|
||
|
|
this._defaultRoughessMap = textureUtil.createBlank('#000');
|
||
|
|
this._debugPass = new Pass({
|
||
|
|
fragment: Shader.source('clay.compositor.output')
|
||
|
|
});
|
||
|
|
|
||
|
|
this._debugPass.setUniform('texture', this._normalTex);
|
||
|
|
|
||
|
|
this._debugPass.material.undefine('fragment', 'OUTPUT_ALPHA');
|
||
|
|
}
|
||
|
|
|
||
|
|
NormalPass.prototype.getDepthTexture = function () {
|
||
|
|
return this._depthTex;
|
||
|
|
};
|
||
|
|
|
||
|
|
NormalPass.prototype.getNormalTexture = function () {
|
||
|
|
return this._normalTex;
|
||
|
|
};
|
||
|
|
|
||
|
|
NormalPass.prototype.update = function (renderer, scene, camera) {
|
||
|
|
var width = renderer.getWidth();
|
||
|
|
var height = renderer.getHeight();
|
||
|
|
var depthTexture = this._depthTex;
|
||
|
|
var normalTexture = this._normalTex;
|
||
|
|
var normalMaterial = this._normalMaterial;
|
||
|
|
depthTexture.width = width;
|
||
|
|
depthTexture.height = height;
|
||
|
|
normalTexture.width = width;
|
||
|
|
normalTexture.height = height;
|
||
|
|
var opaqueList = scene.getRenderList(camera).opaque;
|
||
|
|
|
||
|
|
this._framebuffer.bind(renderer);
|
||
|
|
|
||
|
|
renderer.gl.clearColor(0, 0, 0, 0);
|
||
|
|
renderer.gl.clear(renderer.gl.COLOR_BUFFER_BIT | renderer.gl.DEPTH_BUFFER_BIT);
|
||
|
|
renderer.gl.disable(renderer.gl.BLEND);
|
||
|
|
renderer.renderPass(opaqueList, camera, {
|
||
|
|
getMaterial: function () {
|
||
|
|
return normalMaterial;
|
||
|
|
},
|
||
|
|
ifRender: function (object) {
|
||
|
|
return object.renderNormal;
|
||
|
|
},
|
||
|
|
beforeRender: getBeforeRenderHook(renderer, this._defaultNormalMap, this._defaultBumpMap, this._defaultRoughessMap, this._normalMaterial),
|
||
|
|
sort: renderer.opaqueSortCompare
|
||
|
|
});
|
||
|
|
|
||
|
|
this._framebuffer.unbind(renderer);
|
||
|
|
};
|
||
|
|
|
||
|
|
NormalPass.prototype.renderDebug = function (renderer) {
|
||
|
|
this._debugPass.render(renderer);
|
||
|
|
};
|
||
|
|
|
||
|
|
NormalPass.prototype.dispose = function (renderer) {
|
||
|
|
this._depthTex.dispose(renderer);
|
||
|
|
|
||
|
|
this._normalTex.dispose(renderer);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default NormalPass;
|