对于投影来说,本质和镜面反射一样。
创建一个模型,然后将该模型的所有顶点通过矩阵坍缩到一个平面上。
然后在对应的平面使用模板缓冲,再将该模型绘制到这个平面上。
当然直接投影的话,地面上的阴影并不像影子,因为他的贴图跟模型是一样,因此我们要把它的贴图换成黑色贴图,半透明也可。

先上图
shadow

转换成投影顶点的矩阵为函数为
[cc lang=”actionscript3″]
//计算光照投影矩阵
public static function getShadowMatrix(n : Vector3D, p : Vector3D, light : Vector3D, out : Vector.) : Vector.
{
n.normalize();
//平行光的话,需要标准化光线方向,并逆转平面法线
if(light.w == 0){
light.normalize();
n.scaleBy(-1);
}
var d:Number=-n.dotProduct(p);
var k:Number=n.x*light.x+n.y*light.y+n.z*light.z+d*light.w;

if(null == out) out = new Vector.();

out.push(k-n.x*light.x, -n.x*light.y, -n.x*light.z, -n.x*light.w);
out.push(-n.y*light.x, k-n.y*light.y, -n.y*light.z, -n.y*light.w);
out.push(-n.z*light.x, -n.z*light.y, k-n.z*light.z, -n.z*light.w);
out.push(-d*light.x, -d*light.y, -d*light.z, k-d*light.w);

return out;
}
[/cc]

之后还是跟渲染镜面反射一样, 开启模板缓冲
[cc lang=”actionscript3″]
/**
* 绘制投影
*/

//关闭深度测试
m_context.setDepthTest(false,Context3DCompareMode.LESS);

//设置模板值为0,之后绘制的三角形会使模板值递增
m_context.setStencilReferenceValue(0);
m_context.setStencilActions(Context3DTriangleFace.BACK,
Context3DCompareMode.EQUAL, Context3DStencilAction.INCREMENT_SATURATE);

m_wall.render(m_cameraMatrix, m_projMatrix, m_shader);

m_context.setStencilReferenceValue(1);

//混合模式为 1 * dest + 0 * 0 = dest. 目标颜色为墙体颜色和阴影颜色的混合
m_context.setBlendFactors(Context3DBlendFactor.SOURCE_COLOR, Context3DBlendFactor.DESTINATION_COLOR);

m_shadowTeapot.render(m_cameraMatrix,m_projMatrix,m_shader);
m_shadowTeapot.moveTo(m_teapot.position.x,m_teapot.position.y,m_teapot.position.z);
m_shadowTeapot.rotation(t, Vector3D.Y_AXIS);

m_context.setCulling(Context3DTriangleFace.FRONT);
[/cc]

绘制完投影后,我们要清空缓存,然后在绘制镜面来填充模板,之后都和镜面反射的render一样了.

[cc lang=”actionscript3″]
/**
* 绘制反射
*/

//清空模板
m_context.clear(0,0,0,1,1,0,Context3DClearMask.STENCIL);

//设置模板值为0,之后绘制的三角形会使模板值递增
m_context.setStencilReferenceValue(0);
m_context.setStencilActions(Context3DTriangleFace.FRONT,
Context3DCompareMode.EQUAL, Context3DStencilAction.INCREMENT_SATURATE);

m_mirrorMesh.render(m_cameraMatrix,m_projMatrix,m_shader);

//混合模式为 1 * dest + 0 * 0 = dest. 目标颜色为镜子颜色和飞船颜色的混合
m_context.setBlendFactors(Context3DBlendFactor.DESTINATION_COLOR, Context3DBlendFactor.ZERO);
m_context.setCulling(Context3DTriangleFace.BACK);
m_context.setStencilReferenceValue(1);

m_mirrorTeapot.render(m_cameraMatrix,m_projMatrix,m_shader);
m_mirrorTeapot.moveTo(m_teapot.position.x,m_teapot.position.y,m_teapot.position.z);
m_mirrorTeapot.rotation(t, Vector3D.Y_AXIS);
[/cc]

demo is here

昨天那一帖的延续. http://www.dreamfairy.cn/blog/index.php/2013/04/30/stencil-buffer-in-stage3d.html

由于模板缓冲总是在进行的,因此,很多并不想被缓冲的物体也会被牵连,结果就是绘制不出,又或者是错误的也被缓冲了.
那么对于无辜者的那些三角形,在绘制前,我们可以设置一个模板检测方式为总是检测,这样模板总是能检测成功,成功条件为不改变模板值,这样为之后要用到的对象正确的来改变它.

同时,对于不同深度的物体,有时候无法被缓冲,因为有可能是他们确实位于某些面之后,这里我们需要关闭深度缓存再绘制,绘制完毕后,再开启深度缓冲.

那么昨天的 renderScene() 函数要做些修改,并且加上一个作为无辜者的大矩形放在后面.
buffer
[cc lang=”actionscript”]
private function onEnter(e:Event) : void
{
renderScene();
}

private function renderScene() : void
{
m_context.clear(0,0,0,1,1,0);

m_context.setDepthTest(true,Context3DCompareMode.LESS);
m_context.setStencilActions(Context3DTriangleFace.FRONT_AND_BACK,
Context3DCompareMode.ALWAYS, Context3DStencilAction.KEEP);
drawBiggerCube();

m_context.setDepthTest(false,Context3DCompareMode.LESS);
m_context.setStencilReferenceValue(0);
m_context.setStencilActions(Context3DTriangleFace.FRONT_AND_BACK,
Context3DCompareMode.EQUAL, Context3DStencilAction.INCREMENT_SATURATE);

drawCube();

m_context.setStencilReferenceValue(1);

drawTriangle();
m_context.present();
}

private function drawBiggerCube() : void
{
t += .1;
m_modelMatrix.identity();
m_modelMatrix.appendTranslation(0,0,-1);
m_modelMatrix.appendScale(2,2,2);
m_finalMatrix.identity();
m_finalMatrix.append(m_modelMatrix);
m_finalMatrix.append(m_viewMatrix);
m_finalMatrix.append(m_projMatrix);

m_context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,m_finalMatrix,true);

m_context.setProgram(m_textureShader);
m_context.setTextureAt(0,brickTexture);
m_context.setVertexBufferAt(0, m_rectVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
m_context.setVertexBufferAt(1,m_rectVertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
m_context.drawTriangles(m_rectIndexBuffer,0,2);
}

private function drawTriangle() : void
{
m_modelMatrix.identity();
m_finalMatrix.identity();
m_finalMatrix.append(m_modelMatrix);
m_finalMatrix.append(m_viewMatrix);
m_finalMatrix.append(m_projMatrix);

m_context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,m_finalMatrix,true);

m_context.setProgram(m_shader);
m_context.setTextureAt(0,null);
m_context.setVertexBufferAt(0, m_triangleVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
m_context.setVertexBufferAt(1,null);
m_context.drawTriangles(m_triangleIndexBuffer,0,1);
}

private var t : Number = 0.0;
private function drawCube() : void
{
t += .1;
m_modelMatrix.identity();
m_modelMatrix.appendTranslation(Math.sin(t),0,0);
m_finalMatrix.identity();
m_finalMatrix.append(m_modelMatrix);
m_finalMatrix.append(m_viewMatrix);
m_finalMatrix.append(m_projMatrix);

m_context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,m_finalMatrix,true);

m_context.setProgram(m_textureShader);
m_context.setTextureAt(0,texture);
m_context.setVertexBufferAt(0, m_rectVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
m_context.setVertexBufferAt(1,m_rectVertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
m_context.drawTriangles(m_rectIndexBuffer,0,2);
}
[/cc]

好了,接下来,就是今天的重点,镜面反射了.
Continue reading