转发,请保持地址:http://blog.csdn.net/stalendp/article/details/50775450
本文介绍OpenGL中的投影,同时讲解一下ZBuffer。先看一下古人是怎么进行透视作画的:
Perspective drawing in the Renaissance: “Man drawing a lute” by Albrecht Dürer, 1525
这么严谨!肯定是德国人了 ;-)
除了Graphic Pipeline外,ZBuffer是我最想介绍的部分了,因为有一些很酷实现都依赖于此(比如Shadow、Field Of View、SSOA)。为了具有说服力,我们以公式推导的形式给出结论。公式推导利用了中学的知识“相似三角形法则”,虽然有些繁琐但并不复杂。另外,文章的组织流程大致是沿着Graphic Pipeline的顺序进行的,其中剖析了一个正方形面片的显示过程。这个过程大致为:点->三角形->光栅化->透视矩阵->属性插值。关于ZBuffer的理解,可以带着如下几个问题进行探索:ZBuffer是什么环节下的产物,有什么特性,可以用在哪些地方。
3D模型在经过Model Transform和View Transform之后(以后介绍),会进入到本文介绍的Projection(投影);投影把View(可视区域,也叫“截头椎体”)转化为一个边长为2的立方体(中心点在立方体的中央,xyz轴的范围都为[-1,1]),这个立方体叫做Normalized device coordinates,简称为NDC。以透视投影为例:
(1) |
(2) |
(7) |
(12) |
OpenGL显示3D模型是以三角形为单位来处理的。我们在屏幕上看到的模型,其实是三角形内部的所对应的像素包含的属性(包括颜色、uv、法线等,参考Built-in Vertex Input Parameters),结合外部光照等其它因素综合计算得到。在显示空间三角形时,首先要更具三个点计算其在屏幕上的投影,然后对三角形内部进行填充,根据算法得到每个像素点的颜色。填充过程的第一步是一个光栅化,具体到某一像素点的颜色,其实就是根据当前像素点的属性,通过相应的算法得到颜色。这种取色的途径可以直接在贴图上取色,或则还要通过某种算法得到(比如光照算法)。取色算法是要依赖于当前像素点的信息的,比如uv,法线等。这些值也是通过插值得到的。
下面将展示插值的计算方法并解释属性差值的方法。
(13) |
(14) |
(16) |
(19) |
inline float4 ComputeScreenPos (float4 pos) { float4 o = pos * 0.5f; #if defined(UNITY_HALF_TEXEL_OFFSET) o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw; #else o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w; #endif o.zw = pos.zw; return o; } // Z buffer to linear 0..1 depth (0 at eye, 1 at far plane) inline float Linear01Depth( float z ) { return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y); } // Z buffer to linear depth inline float LinearEyeDepth( float z ) { return 1.0 / (_ZBufferParams.z * z + _ZBufferParams.w); }
// Depth pass vertex shader output.vPositionCS = mul(input.vPositionOS, g_matWorldViewProj); output.vDepthCS.xy = output.vPositionCS.zw; // Depth pass pixel shader (output z/w) return input.vDepthCS.x / input.vDepthVS.y; // Function for converting depth to view-space position // in deferred pixel shader pass. vTexCoord is a texture // coordinate for a full-screen quad, such that x=0 is the // left of the screen, and y=0 is the top of the screen. float3 VSPositionFromDepth(float2 vTexCoord) { // Get the depth value for this pixel float z = tex2D(DepthSampler, vTexCoord); // Get x/w and y/w from the viewport position float x = vTexCoord.x * 2 - 1; float y = (1 - vTexCoord.y) * 2 - 1; float4 vProjectedPos = float4(x, y, z, 1.0f); // Transform by the inverse projection matrix float4 vPositionVS = mul(vProjectedPos, g_matInvProjection); // Divide by w to get the view-space position return vPositionVS.xyz / vPositionVS.w; }
1)《Mathematics for 3D Game Programming and Computer Graphics(3rd Edition)》Chapter 5.5 Projections.
2)《Real-Time Rendering(3rd Edition)》Chapter 4.6 Projections
3) Cg Programming/Vertex Transformations
4) Cg Programming/Rasterization
5)Cg Programming/Unity/Debugging of Shaders
6)Using Depth Texture
7)Built-in shader variables (也可以参考官方buildin shader中的 “UnityShaderVariables.cginc”)
8)Scintillating Snippets: Reconstructing Position From Depth
9)Reconstructing pixel 3D position from depth
10) Camera’s Depth Texture