我有一些关于在WebGL场景中绘制.obj阴影的问题。 例如,如果我想用Shadow Volumes Method绘制阴影,我应该如何开发它?我试图实现这一点,但我失败了。是否有更有效的方法来执行此操作(编写更少的代码)? 以下是代码:
function createShadowBuilder(item) {
var that = function() {};
that.init = function(item) {
this.item = item;
this.glPositionBuffer = null;
this.glVertexIndexBuffer = null;
};
that.setupData = function() {
if (this.glPositionBuffer !== null) {
gl.deleteBuffer(this.glPositionBuffer);
}
if (this.glVertexIndexBuffer !== null) {
gl.deleteBuffer(this.glVertexIndexBuffer);
}
this.glVertices = [];
this.glIndices = [];
};
that.addGLVertex = function(vector) {
this.glVertices.push(vector[0]);
this.glVertices.push(vector[1]);
this.glVertices.push(vector[2]);
this.glIndices.push(this.glIndices.length);
};
that.addShadowSide = function(vector1, vector2, vector3, vector4) {
this.addGLVertex(vector1);
this.addGLVertex(vector2);
this.addGLVertex(vector3);
this.addGLVertex(vector4);
this.addGLVertex(vector3);
this.addGLVertex(vector2);
};
/**
* Check which triangles face the light source...
**/
that.checkDirection = function(lightLocation) {
var triangles = this.item.triangles,
triangle,
vector,
i = triangles.length;
while (i) {
i--;
// Create a normalized vector based on the vector from
// the center of the triangle to the lights position...
triangle = triangles[i];
vector = vec3.create(triangle.center);
vector = vec3.normalize(vec3.subtract(vector, lightLocation));
// Compare the vector with the normal of the triangle...
triangle.visible = (vec3.dot(vector, triangle.normal) < 0);
}
}
/**
* Find the edge of the object...
**/
that.findEdge = function() {
var triangles = this.item.triangles,
triangle,
a, b,
lines = this.item.lines,
line,
lineSidesHash = {},
i, j, k;
this.lineSides = [];
i = triangles.length;
while (i) {
i--;
triangle = triangles[i];
if (triangle.visible) {
j = 3;
while (j) {
j--;
// Check if the side...
k = triangle.lines[j];
line = lines[k];
a = line.v1 + '_' + line.v2;
b = line.v2 + '_' + line.v1;
if (lineSidesHash[a] !== undefined) { // Check the v1 -> v2 direction...
// The side already exists, remove it...
delete(lineSidesHash[a]);
}
else if (lineSidesHash[b] !== undefined) { // Check the v2 -> v1 direction...
// The side already exists, remove it...
delete(lineSidesHash[b]);
}
else {
// It's a new side, add it to the list...
lineSidesHash[a] = k;
}
}
}
}
// Convert the hash map to an array...
for (i in lineSidesHash) {
line = lines[lineSidesHash[i]];
this.lineSides.push(line);
}
};
that.rotateVectorX = function(vector, angle) {
var x, y,
sin, cos;
if (angle === 0) {
return;
}
y = vector[1];
z = vector[2];
sin = Math.sin(angle);
cos = Math.cos(angle);
vector[1] = y * cos - z * sin;
vector[2] = y * sin + z * cos;
};
that.rotateVectorY = function(vector, angle) {
var x, z,
sin, cos;
if (angle === 0) {
return;
}
x = vector[0];
z = vector[2];
sin = Math.sin(angle);
cos = Math.cos(angle);
vector[0] = z * sin + x * cos;
vector[2] = z * cos - x * sin;
};
that.rotateVectorZ = function(vector, angle) {
var x, y,
sin, cos;
if (angle === 0) {
return;
}
x = vector[0];
y = vector[1];
sin = Math.sin(angle);
cos = Math.cos(angle);
vector[0] = x * cos - y * sin;
vector[1] = x * sin + y * cos;
};
/**
* Update the shadow...
**/
that.update = function(lightLocation, lightAngle, matrix, zoom) {
// Get the position of the light from the matrix, remove the zoom value...
var vector = vec3.subtract(vec3.create(lightLocation), [matrix[12], matrix[13], matrix[14] + zoom]),
sin, cos,
x, y, z;
// Instead of rotating the object to face the light at the
// right angle it's a lot faster to rotate the light in the
// reverse direction...
this.rotateVectorX(vector, -lightAngle[0]);
this.rotateVectorY(vector, -lightAngle[1]);
this.rotateVectorZ(vector, -lightAngle[2]);
// Store the location for later use...
this.lightLocation = vector;
this.setupData(); // Reset all lists and buffers...
this.checkDirection(vector); // Check which triangles face the light source...
this.findEdge(); // Find the edge...
};
/**
* Create the buffers for the shadow volume...
**/
that.createVolume = function(lightLocation) {
var vertices = this.item.vertices,
triangles = this.item.triangles,
triangle,
lineSides = this.lineSides,
line,
vector1, vector2, vector3, vector4,
i = lineSides.length,
j;
while (i) { // For all edge lines...
i--;
line = lineSides[i];
vector1 = vertices[line.v1];
vector2 = vertices[line.v2];
// Extrude the line away from the light...
// Get the vector from the light position to the vertex...
vector3 = vec3.subtract(vector1, lightLocation, vec3.create());
// Add the normalized vector scaled with the volume
// depth to the vertex which gives a point on the other
// side of the object than the light source...
vector3 = vec3.add(vec3.scale(vec3.normalize(vector3), 30), vector1);
// And again for the second point on the line...
vector4 = vec3.subtract(vector2, lightLocation, vec3.create());
vector4 = vec3.add(vec3.scale(vec3.normalize(vector4), 30), vector2);
this.addShadowSide(vector1, vector2, vector3, vector4);
}
// Add the end caps to the volume...
i = triangles.length;
while (i) {
i--;
triangle = triangles[i];
if (triangle.visible) { // Only add polygons facing the light...
// Add the top...
j = 3;
while (j) {
j--;
this.addGLVertex(vertices[triangle.vertices[j]]);
}
// Add the bottom...
j = 0;
while (j < 3) {
vector1 = vertices[triangle.vertices[j]];
vector2 = vec3.subtract(vector1, lightLocation, vec3.create());
this.addGLVertex(vec3.add(vec3.scale(vec3.normalize(vector2), 30), vector1));
j++;
}
}
}
// Create the vertex position buffer...
this.glPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.glPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.glVertices), gl.STATIC_DRAW);
this.glPositionBuffer.itemSize = 3;
// Create the vertex index buffer...
this.glVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.glIndices), gl.STATIC_DRAW);
this.glVertexIndexBuffer.numItems = this.glIndices.length;
};
that.render = function() {
// Create the volume for the light...
this.createVolume(this.lightLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, this.glPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, this.glPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glVertexIndexBuffer);
setMatrixUniforms();
// Disable the texture coord attribute...
gl.disableVertexAttribArray(shaderProgram.textureCoordAttribute);
// Disable the normal attribute...
gl.disableVertexAttribArray(shaderProgram.vertexNormalAttribute);
// Disable the color attribute...
gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
// Render both front and back facing polygons with different stencil operations...
gl.disable(gl.CULL_FACE);
gl.enable(gl.STENCIL_TEST);
gl.depthFunc(gl.LESS);
// Disable rendering to the color buffer...
gl.colorMask(false, false, false, false);
// Disable z buffer updating...
gl.depthMask(false);
// Allow all bits in the stencil buffer...
gl.stencilMask(255);
// Increase the stencil buffer for back facing polygons, set the z pass opperator
gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.INCR);
// Decrease the stencil buffer for front facing polygons, set the z pass opperator
gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.DECR);
// Always pass...
gl.stencilFunc(gl.ALWAYS, 0, 255);
gl.drawElements(gl.TRIANGLES, this.glVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
// Enable rendering the the color and depth buffer again...
gl.colorMask(true, true, true, true);
gl.depthMask(true);
gl.disable(gl.STENCIL_TEST);
};
that.init(item);
return that;
}
这段代码来自网络上的一个例子,我试图改编它。