Her şeyden önce, derinlik haritaları ve küp haritaları kullanarak gölge haritalama hakkında birçok yazı okuduğumu ve nasıl çalıştıklarını anladığımı ve ayrıca OpenGL kullanarak onlarla çalışma deneyimim olduğunu söylemek istiyorum, ancak uygulamada bir sorunum var 3D grafik motorumda "EZ3" adlı tek noktalı bir ışık kaynağı kullanan çok yönlü Gölge Eşleme tekniği. Motorum WebGL'yi 3D grafik API ve JavaScript'i programlama dili olarak kullanıyor, bu benim lisansımın Bilgisayar Bilimi tezi için.
Temel olarak gölge haritalama algoritmamı bu şekilde uyguladım, ancak sadece nokta ışıkları durumuna odaklanacağım çünkü onlarla çok yönlü gölge haritalamayı arşivleyebilirim.
İlk olarak, ön yüz culling böyle aktif:
if (this.state.faceCulling !== Material.FRONT) {
if (this.state.faceCulling === Material.NONE)
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
this.state.faceCulling = Material.FRONT;
}
İkincisi, her cubemap yüzü için derinlik değerleri kaydetmek için bir derinlik programı oluşturuyorum, bu GLSL 1.0'daki derinlik program kodum:
Köşe Gölgelendirici:
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
Fragman Gölgelendiricisi:
precision highp float;
vec4 packDepth(const in float depth) {
const vec4 bitShift = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
const vec4 bitMask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 res = mod(depth * bitShift * vec4(255), vec4(256)) / vec4(255);
res -= res.xxyz * bitMask;
return res;
}
void main() {
gl_FragData[0] = packDepth(gl_FragCoord.z);
}
Üçüncüsü, bu benim çok fonksiyonlu gölge eşlemesini "arşivleyen" JavaScript işlevimin gövdesi
program.bind(gl);
for (i = 0; i < lights.length; i++) {
light = lights[i];
// Updates pointlight's projection matrix
light.updateProjection();
// Binds point light's depth framebuffer
light.depthFramebuffer.bind(gl);
// Updates point light's framebuffer in order to create it
// or if it's resolution changes, it'll be created again.
light.depthFramebuffer.update(gl);
// Sets viewport dimensions with depth framebuffer's dimensions
this.viewport(new Vector2(), light.depthFramebuffer.size);
if (light instanceof PointLight) {
up = new Vector3();
view = new Matrix4();
origin = new Vector3();
target = new Vector3();
for (j = 0; j < 6; j++) {
// Check in which cubemap's face we are ...
switch (j) {
case Cubemap.POSITIVE_X:
target.set(1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_X:
target.set(-1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.POSITIVE_Y:
target.set(0, 1, 0);
up.set(0, 0, 1);
break;
case Cubemap.NEGATIVE_Y:
target.set(0, -1, 0);
up.set(0, 0, -1);
break;
case Cubemap.POSITIVE_Z:
target.set(0, 0, 1);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_Z:
target.set(0, 0, -1);
up.set(0, -1, 0);
break;
}
// Creates a view matrix using target and up vectors according to each face of pointlight's
// cubemap. Furthermore, I translate it in minus light position in order to place
// the point light in the world's origin and render each cubemap's face at this
// point of view
view.lookAt(origin, target, up);
view.mul(new EZ3.Matrix4().translate(light.position.clone().negate()));
// Flips the Y-coordinate of each cubemap face
// scaling the projection matrix by (1, -1, 1).
// This is a perspective projection matrix which has:
// 90 degress of FOV.
// 1.0 of aspect ratio.
// Near clipping plane at 0.01.
// Far clipping plane at 2000.0.
projection = light.projection.clone();
projection.scale(new EZ3.Vector3(1, -1, 1));
// Attaches a cubemap face to current framebuffer in order to record depth values for the face with this line
// gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + j, id, 0);
light.depthFramebuffer.texture.attach(gl, j);
// Clears current framebuffer's color with these lines:
// gl.clearColor(1.0,1.0,1.0,1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
this.clear(color);
// Renders shadow caster meshes using the depth program
for (k = 0; k < shadowCasters.length; k++)
this._renderShadowCaster(shadowCasters[k], program, view, projection);
}
} else {
// Directional light & Spotlight case ...
}
}
Dördüncüsü, ana Vertex Shader ve Fragment Shader'ımdaki derinlik cubemap'ımı kullanarak Çok Yönlü Gölge Eşlemesini şu şekilde hesaplıyorum:
Köşe Gölgelendirici:
precision highp float;
attribute vec3 position;
uniform mat4 uModel;
uniform mat4 uModelView;
uniform mat4 uProjection;
varying vec3 vPosition;
void main() {
vPosition = vec3(uModel * vec4(position, 1.0));
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
Fragman Gölgelendiricisi:
float unpackDepth(in vec4 color) {
return dot(color, vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ));
}
float pointShadow(const in PointLight light, const in samplerCube shadowSampler) {
vec3 direction = vPosition - light.position;
float vertexDepth = clamp(length(direction), 0.0, 1.0);
float shadowMapDepth = unpackDepth(textureCube(shadowSampler, direction));
return (vertexDepth > shadowMapDepth) ? light.shadowDarkness : 1.0;
}
Sonunda, elde ettiğim sonuç bu, sahnemde bir uçak, bir küp ve bir küre var. Ayrıca, kırmızı parlak küre nokta ışık kaynağıdır:
Gördüğünüz gibi, nokta ışık derinliği çerçevesinin kübap haritasının yüzleri arasında iyi bir enterpolasyon yapmaması gibi görünüyor.
Şimdiye kadar, bunu nasıl çözeceğime dair hiçbir fikrim yok.