本章可以學(xué)到如何使用深度測(cè)試,以及參數(shù)選擇的效果
在繪制3D 圖像的時(shí)候,我們需要開啟深度測(cè)試, 來防止被阻擋的面渲染到其它面的前面
. 本章將深入地討論這些儲(chǔ)存在深度緩沖(或z緩沖(z-buffer))中的深度值(Depth Value)迷殿,以及它們是如何確定一個(gè)片段是處于其它片段后方的厂镇。
深度緩沖就像顏色緩沖(Color Buffer)(儲(chǔ)存所有的片段顏色:視覺輸出)一樣隧饼,在每個(gè)片段中儲(chǔ)存了信息,并且(通常)和顏色緩沖有著一樣的寬度和高度。深度緩沖是由系統(tǒng)自動(dòng)創(chuàng)建的
炸枣,它會(huì)以16、24或32位float的形式儲(chǔ)存它的深度值。在大部分的系統(tǒng)中旁蔼,深度緩沖的精度都是24位的。
當(dāng)深度測(cè)試(Depth Testing)被啟用的時(shí)候疙教,OpenGL會(huì)將一個(gè)片段的的深度值與深度緩沖的內(nèi)容進(jìn)行對(duì)比棺聊。OpenGL會(huì)執(zhí)行一個(gè)深度測(cè)試,如果這個(gè)測(cè)試通過了的話贞谓,深度緩沖將會(huì)更新為新的深度值限佩。如果深度測(cè)試失敗了,片段將會(huì)被丟棄。
深度緩沖是在片段著色器運(yùn)行之后以及模板測(cè)試(Stencil Testing)運(yùn)行之后在屏幕空間中運(yùn)行的祟同。
屏幕空間坐標(biāo)與通過OpenGL的glViewport所定義的視口密切相關(guān)作喘,并且可以直接使用GLSL內(nèi)建變量gl_FragCoord從片段著色器中直接訪問。gl_FragCoord的x和y分量代表了片段的屏幕空間坐標(biāo)(其中(0, 0)位于左下角)晕城。gl_FragCoord中也包含了一個(gè)z分量泞坦,它包含了片段真正的深度值。z值就是需要與深度緩沖內(nèi)容所對(duì)比的那個(gè)值砖顷。
現(xiàn)在大部分的GPU都提供一個(gè)叫做提前深度測(cè)試(Early Depth Testing)的硬件特性贰锁。提前深度測(cè)試允許深度測(cè)試在片段著色器之前運(yùn)行。只要我們清楚一個(gè)片段永遠(yuǎn)不會(huì)是可見的(它在其他物體之后)择吊,我們就能提前丟棄這個(gè)片段李根。
片段著色器通常開銷都是很大的,所以我們應(yīng)該盡可能避免運(yùn)行它們
几睛。當(dāng)使用提前深度測(cè)試時(shí)房轿,片段著色器的一個(gè)限制是你不能寫入片段的深度值。如果一個(gè)片段著色器對(duì)它的深度值進(jìn)行了寫入所森,提前深度測(cè)試是不可能的囱持。OpenGL不能提前知道深度值。
深度測(cè)試默認(rèn)是禁用的焕济,所以如果要啟用深度測(cè)試的話纷妆,我們需要用GL_DEPTH_TEST
選項(xiàng)來啟用它:
glEnable(GL_DEPTH_TEST);
當(dāng)它啟用的時(shí)候,如果一個(gè)片段通過了深度測(cè)試的話晴弃,OpenGL會(huì)在深度緩沖中儲(chǔ)存該片段的z值掩幢;如果沒有通過深度緩沖,則會(huì)丟棄該片段上鞠。如果你啟用了深度緩沖际邻,你還應(yīng)該在每個(gè)渲染迭代之前使用GL_DEPTH_BUFFER_BIT來清除深度緩沖,否則你會(huì)仍在使用上一次渲染迭代中的寫入的深度值:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
可以想象芍阎,在某些情況下你會(huì)需要對(duì)所有片段都執(zhí)行深度測(cè)試并丟棄相應(yīng)的片段世曾,但不希望更新深度緩沖∏聪蹋基本上來說轮听,你在使用一個(gè)只讀的(Read-only)深度緩沖。OpenGL允許我們禁用深度緩沖的寫入岭佳,只需要設(shè)置它的深度掩碼(Depth Mask)設(shè)置為GL_FALSE就可以了:
glDepthMask(GL_FALSE);
注意這只在深度測(cè)試被啟用的時(shí)候才有效果血巍。
開啟深度測(cè)試和關(guān)閉深度測(cè)試的效果
我們繪制一個(gè)正方體來測(cè)試開啟深度測(cè)試和關(guān)閉深度測(cè)試的效果
基礎(chǔ)類代碼
正方體坐標(biāo)
float cubeVerts[]={
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5,-0.5, 0.5,
0.5,-0.5, 0.5,
0.5, 0.5, -0.5,
-0.5, 0.5, -0.5,
-0.5,-0.5,-0.5,
0.5,-0.5,-0.5,
};
float cubeColors[]={
1.0,0.0,0.0,
1.0,0.0,0.0,
1.0,0.0,0.0,
1.0,0.0,0.0,
0.0,1.0,0.0,
0.0,1.0,0.0,
0.0,1.0,0.0,
0.0,1.0,0.0,
};
#import "DepthTestIsOpenViewController.h"
#import "DepthTestIsOpenBindObject.h"
#import "CubeManager.h"
@interface DepthTestIsOpenViewController ()
@property (nonatomic ,strong) Vertex * vertex;
@property (nonatomic ,strong) VertexElement * vertexElement ;
@end
@implementation DepthTestIsOpenViewController
-(void)initSubObject{
self.bindObject = [DepthTestIsOpenBindObject new];
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT);
};
}
-(void)loadVertex{
self.vertex= [Vertex new];
int vertexNum =[CubeManager getVertexNum];
[self.vertex allocVertexNum:vertexNum andEachVertexNum:6];
for (int i=0; i<vertexNum; i++) {
float onevertex[6];
for (int j=0; j<3; j++) {
onevertex[j]=[CubeManager getCubeVerts][i*3+j];
}
for (int j=0; j<3; j++) {
onevertex[j+3]=[CubeManager cubeColors][i*3+j];
}
[self.vertex setVertex:onevertex index:i];
}
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:DTIO_aPos numberOfCoordinates:3 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTIO_aColor numberOfCoordinates:3 attribOffset:sizeof(float)*3];
self.vertexElement = [VertexElement new];
[self.vertexElement allocWithIndexNum:[CubeManager getVertexElementsNum] indexArr:[CubeManager getVertexElements]];
}
-(void)createTextureUnit{
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(1.0,1.0,1.0, 1);
self.glDrawConfig();
GLKMatrix4 viewMatrix =
GLKMatrix4MakeLookAt(
0.0, 0.0, 3.0, // Eye position
0.0, 0.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
glUniformMatrix4fv(self.bindObject->uniforms[DTIO_uniform_view], 1, 0,viewMatrix.m);
GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
GLKMatrix4 projectionMatrix =
GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(85.0f),
aspectRatio,
0.1f,
100.0f);
glUniformMatrix4fv(self.bindObject->uniforms[DTIO_uniform_projection], 1, 0,projectionMatrix.m);
GLKMatrix4 mode = GLKMatrix4Identity;
static GLfloat angle=30;
angle +=3;
mode = GLKMatrix4Rotate(mode,angle*M_PI/180, 0, 1, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DTIO_uniform_model], 1, 0,mode.m);
[self.vertexElement drawElementIndexWithMode:GL_TRIANGLES];
}
@end
#import "GLBaseViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface DepthTestIsOpenViewController : GLBaseViewController
@property (nonatomic ,strong) void(^glDrawConfig)(void) ;
@end
NS_ASSUME_NONNULL_END
shader
attribute vec3 aPos;
attribute vec3 aColor;
varying vec3 vColor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main (){
vColor = aColor;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
precision mediump float;//mediump
varying vec3 vColor;
void main(){
lowp vec4 rgba = vec4(vColor,1);
gl_FragColor = rgba;
}
#import "GLBaseViewController.h"
@interface GLBaseViewController ()
@property (nonatomic ,strong) TextureUnit * textureUnit0 ;
@end
@implementation GLBaseViewController
-(void)createEagContext{
self.eagcontext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.eagcontext];
}
-(void)configure{
GLKView *view = (GLKView*)self.view;
view.context = self.eagcontext;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
}
-(void)initCustom{
}
-(void)initSubObject{
}
-(void)createTextureUnit{
}
-(void)createShader{
self.shader = [Shader new];
[self.shader compileLinkSuccessShaderName:self.bindObject.getShaderName completeBlock:^(GLuint program) {
[self.bindObject BindAttribLocation:program];
}];
[self.bindObject setUniformLocation:self.shader.program];
}
-(void)loadVertex{
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
}
-(void)viewDidLoad{
[super viewDidLoad];
[self createEagContext];
[self initSubObject];
[self configure];
[self initCustom];
[self createShader];
[self createTextureUnit];
[self loadVertex];
}
@end
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import "OpenGLUtilsHeader.h"
#import "GLBaseBindObject.h"
NS_ASSUME_NONNULL_BEGIN
@interface GLBaseViewController : GLKViewController
@property (nonatomic ,strong) EAGLContext * eagcontext;
@property (nonatomic ,assign) GLuint program;
@property (nonatomic ,strong) Shader * shader ;
@property (nonatomic ,strong) GLBaseBindObject * bindObject ;
///眼的位置在 0,0,1 看向 原點(diǎn) ,眼的正方向是y軸, 看的區(qū)域是0.1 到20 角度是85
-(GLKMatrix4 )mvp;
-(void)loadVertex;
-(void)initSubObject;
-(void)createTextureUnit;
@end
我這里只貼了主要的代碼. 代碼不全,可以下載demo來看.
未開啟深度測(cè)試效果
我們發(fā)現(xiàn) 正方體是部分鏤空.
開啟深度測(cè)試效果
可以正常展示一個(gè)正方體了
#import "DepthTestOpenViewController.h"
@interface DepthTestOpenViewController ()
@end
@implementation DepthTestOpenViewController
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
}
@end
glDepthMask的使用
該函數(shù)起作用必須是我們要開啟深度測(cè)試才行
測(cè)試代碼做的事情是我們繪制三張圖片,三張圖片按照 綠色圖片 ,箱子,紅色圖片的順序繪制. 我們看glDepthMask對(duì)三張圖片的影響
核心測(cè)試代碼
#import "DepthTestMaskViewController.h"
#import "DepthTestMaskBindObject.h"
@interface DepthTestMaskViewController ()
@property (nonatomic ,strong) Vertex * vertex;
@property (nonatomic ,strong) TextureUnit * unitFront ;
@property (nonatomic ,strong) TextureUnit * unitMiddle;
@property (nonatomic ,strong) TextureUnit * unitBack;
@end
@implementation DepthTestMaskViewController
-(void)initSubObject{
//生命周期三秒鐘
self.bindObject = [DepthTestMaskBindObject new];
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT);
};
self.glDrawMaskEndConfig = ^{
};
self.glDrawMaskBeginConfig = ^{
};
}
#define VertexNum 4
#define eachVertexNum 4
static GLfloat s_vertex[4*4] = {
0.5,0.5, 0,1, //1
-0.5,0.5,1,1,//0
-0.5,-0.5,1,0, //2
0.5,-0.5,0,0//3
};
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:eachVertexNum];
for (int i =0; i<VertexNum; i++) {
GLfloat vertex[4];
vertex[0] = s_vertex[i*VertexNum];
vertex[1] = s_vertex[i*VertexNum+1];
vertex[2] = s_vertex[i*VertexNum+2];
vertex[3] = s_vertex[i*VertexNum+3];
[self.vertex setVertex:vertex index:i];
}
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:DTM_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTM_aTexture numberOfCoordinates:2 attribOffset:2*sizeof(GLfloat)];
}
-(void)createTextureUnit{
self.unitFront = [TextureUnit new];
[self.unitFront setImage:[UIImage imageNamed:@"green.png"] IntoTextureUnit:GL_TEXTURE0 andConfigTextureUnit:nil];
self.unitBack = [TextureUnit new];
[self.unitBack setImage:[UIImage imageNamed:@"red.png"] IntoTextureUnit:GL_TEXTURE1 andConfigTextureUnit:nil];
self.unitMiddle = [TextureUnit new];
[self.unitMiddle setImage:[UIImage imageNamed:@"texture.jpg"] IntoTextureUnit:GL_TEXTURE2 andConfigTextureUnit:nil];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(1,1,1, 1);
self.glDrawConfig();
GLKMatrix4 viewMatrix =
GLKMatrix4MakeLookAt(
0.0, 0.0, 3.0, // Eye position
0.0, 0.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
glUniformMatrix4fv(self.bindObject->uniforms[DTM_uniform_view], 1, 0,viewMatrix.m);
GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
GLKMatrix4 projectionMatrix =
GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(85.0f),
aspectRatio,
0.1f,
10.0);
glUniformMatrix4fv(self.bindObject->uniforms[DTM_uniform_projection], 1, 0,projectionMatrix.m);
//front
{
GLKMatrix4 mode = GLKMatrix4Identity;
glUniformMatrix4fv(self.bindObject->uniforms[DTM_uniform_model], 1, 0,mode.m);
[self.unitFront bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTM_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTM_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTM_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:VertexNum];
}
// middle
self.glDrawMaskBeginConfig();
{
GLKMatrix4 mode = GLKMatrix4Identity;
mode = GLKMatrix4Translate(mode, 0.25, 0, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DTM_uniform_model], 1, 0,mode.m);
[self.unitMiddle bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTM_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTM_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTM_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:VertexNum];
}
//back
{
GLKMatrix4 mode = GLKMatrix4Identity;
mode = GLKMatrix4Translate(mode, 0.5, 0, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DTM_uniform_model], 1, 0,mode.m);
[self.unitBack bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTM_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTM_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTM_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:VertexNum];
}
self.glDrawMaskEndConfig();
}
@end
這里我們需要說下,深度值其實(shí)就是z值.深度測(cè)試就是測(cè)試z值的大小.
不開啟深度測(cè)試的測(cè)試效果
我們發(fā)現(xiàn) 圖片的圖層順序是 綠色最下,箱子中間 ,紅色圖片在上.
開啟深度測(cè)試測(cè)測(cè)試效果
我們發(fā)現(xiàn)圖片的順序正好和未開啟深度測(cè)試的順序是相反的.綠色在上,箱子中間 ,紅色在下.
這是因?yàn)?當(dāng)開始測(cè)試的時(shí)候,繪制的時(shí)候就需要進(jìn)行深度測(cè)試的比較了.
我們先繪制綠色圖片,綠色圖片所在屏幕上的像素點(diǎn)的深度值就大于0.
當(dāng)我們繪制箱子的時(shí)候,箱子和綠色重疊的部分深度值等于綠色圖片不繪制,而只繪制和綠色圖片不重疊的部分
紅色的圖片同樣的道理繪制
這里需要注意,我們繪制綠色圖片的時(shí)候,坐標(biāo)z軸是0,但是深度值不是0
沒有繪制的顏色的區(qū)域的深度值是0
深度值越小越繪制
圖例如下
開始深度測(cè)試并且使用遮罩效果
代碼如下
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthMask(GL_FALSE);
};
self.glDrawMaskEndConfig = ^{
glDepthMask(GL_TRUE);
};
}
代碼具體做的事情如下
- 1.我們開啟 glEnable(GL_DEPTH_TEST); 和 glDepthMask(GL_TRUE);
- 2.繪制了綠色圖片
- 3.關(guān)閉 glDepthMask(GL_FALSE);
- 4.繪制箱子和紅色圖片 最終的結(jié)果如上
具體行為分析如下
當(dāng)開啟測(cè)試的時(shí)候并且遮罩(mask)開啟的時(shí)候,繪制的時(shí)候就需要進(jìn)行深度測(cè)試的比較了.
我們先繪制綠色圖片,綠色圖片所在屏幕上的像素點(diǎn)的深度值就大于0.
關(guān)閉mask,繪制箱子的時(shí)候,箱子對(duì)應(yīng)的深度值是0,那么,箱子和綠色重疊的部分進(jìn)行深度值比較,明顯小于深小于綠色圖片的深度值,不繪制,而只繪制和綠色圖片不重疊的部分,該部分深度值是默認(rèn)值.
接著繪制紅色圖片,紅色圖片和箱子重疊部分,由于該部分的深度值是默認(rèn)值,因此,紅色覆蓋該像素點(diǎn)了.
圖例
深度測(cè)試函數(shù)
OpenGL允許我們修改深度測(cè)試中使用的比較運(yùn)算符。這允許我們來控制OpenGL什么時(shí)候該通過或丟棄一個(gè)片段驼唱,什么時(shí)候去更新深度緩沖藻茂。我們可以調(diào)用glDepthFunc函數(shù)來設(shè)置比較運(yùn)算符(或者說深度函數(shù)(Depth Function)):
glDepthFunc(GL_LESS);
這個(gè)函數(shù)接受下面表格中的比較運(yùn)算符:
函數(shù) | 描述 |
---|---|
GL_ALWAYS | 永遠(yuǎn)通過深度測(cè)試 |
GL_NEVER | 永遠(yuǎn)不通過深度測(cè)試 |
GL_LESS | 在片段深度值小于緩沖的深度值時(shí)通過測(cè)試 |
GL_EQUAL | 在片段深度值等于緩沖區(qū)的深度值時(shí)通過測(cè)試 |
GL_LEQUAL | 在片段深度值小于等于緩沖區(qū)的深度值時(shí)通過測(cè)試 |
GL_GREATER | 在片段深度值大于緩沖區(qū)的深度值時(shí)通過測(cè)試 |
GL_NOTEQUAL | 在片段深度值不等于緩沖區(qū)的深度值時(shí)通過測(cè)試 |
GL_GEQUAL | 在片段深度值大于等于緩沖區(qū)的深度值時(shí)通過測(cè)試 |
默認(rèn)情況下使用的深度函數(shù)是GL_LESS,它將會(huì)丟棄深度值大于等于當(dāng)前深度緩沖值的所有片段。
測(cè)試基本代碼
#import "DepthTestFuncViewController.h"
#import "DepthTestFuncBindObject.h"
@interface DepthTestFuncViewController ()
@property (nonatomic ,strong) Vertex * vertex;
@property (nonatomic ,strong) TextureUnit * unitFront ;
@property (nonatomic ,strong) TextureUnit * unitMiddle;
@property (nonatomic ,strong) TextureUnit * unitBack;
@end
@implementation DepthTestFuncViewController
-(void)initSubObject{
//生命周期三秒鐘
self.bindObject = [DepthTestFuncBindObject new];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskEndConfig = ^{
};
self.glDrawMaskBeginConfig = ^{
};
}
#define DTF_VertexNum 4
#define DTF_eachVertexNum 4
static GLfloat dtf_s_vertex[4*4] = {
0.5,0.5, 0,1, //1
-0.5,0.5,1,1,//0
-0.5,-0.5,1,0, //2
0.5,-0.5,0,0//3
};
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:DTF_VertexNum andEachVertexNum:DTF_eachVertexNum];
for (int i =0; i<DTF_VertexNum; i++) {
GLfloat vertex[4];
vertex[0] = dtf_s_vertex[i*DTF_VertexNum];
vertex[1] = dtf_s_vertex[i*DTF_VertexNum+1];
vertex[2] = dtf_s_vertex[i*DTF_VertexNum+2];
vertex[3] = dtf_s_vertex[i*DTF_VertexNum+3];
[self.vertex setVertex:vertex index:i];
}
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:DTF_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTF_aTexture numberOfCoordinates:2 attribOffset:2*sizeof(GLfloat)];
}
-(void)createTextureUnit{
self.unitFront = [TextureUnit new];
[self.unitFront setImage:[UIImage imageNamed:@"green.png"] IntoTextureUnit:GL_TEXTURE0 andConfigTextureUnit:nil];
self.unitBack = [TextureUnit new];
[self.unitBack setImage:[UIImage imageNamed:@"red.png"] IntoTextureUnit:GL_TEXTURE1 andConfigTextureUnit:nil];
self.unitMiddle = [TextureUnit new];
[self.unitMiddle setImage:[UIImage imageNamed:@"texture.jpg"] IntoTextureUnit:GL_TEXTURE2 andConfigTextureUnit:nil];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(1,1,1, 1);
self.glDrawConfig();
GLKMatrix4 viewMatrix =
GLKMatrix4MakeLookAt(
0.0, 0.0, 3.0, // Eye position
0.0, 0.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
glUniformMatrix4fv(self.bindObject->uniforms[DTF_uniform_view], 1, 0,viewMatrix.m);
GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
GLKMatrix4 projectionMatrix =
GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(85.0f),
aspectRatio,
0.1f,
10.0);
glUniformMatrix4fv(self.bindObject->uniforms[DTF_uniform_projection], 1, 0,projectionMatrix.m);
self.glDrawMaskBeginConfig();
//front
{
GLKMatrix4 mode = GLKMatrix4Identity;
glUniformMatrix4fv(self.bindObject->uniforms[DTF_uniform_model], 1, 0,mode.m);
[self.unitFront bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTF_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTF_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTF_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:DTF_eachVertexNum];
}
// middle
{
GLKMatrix4 mode = GLKMatrix4Identity;
mode = GLKMatrix4Translate(mode, 0.25, 0, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DTF_uniform_model], 1, 0,mode.m);
[self.unitMiddle bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTF_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTF_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTF_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:DTF_eachVertexNum];
}
self.glDrawMaskEndConfig();
//back
{
GLKMatrix4 mode = GLKMatrix4Identity;
mode = GLKMatrix4Translate(mode, 0.5, 0, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DTF_uniform_model], 1, 0,mode.m);
[self.unitBack bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DTF_uniform_sam2D]];
[self.vertex enableVertexInVertexAttrib:DTF_aPos numberOfCoordinates:2 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DTF_aTexture numberOfCoordinates:2 attribOffset:sizeof(float)*2];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:DTF_eachVertexNum];
}
}
@end
該測(cè)試代碼和測(cè)試mask代碼比較,只是更改了glDrawMaskBeginConfig
和glDrawMaskEndConfig
的測(cè)試位置
GL_ALWAYS
永遠(yuǎn)通過深度測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
};
}
測(cè)試結(jié)果
GL_NEVER
永遠(yuǎn)不通過深度測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_NEVER);
};
}
測(cè)試結(jié)果
GL_LESS
在片段深度值小于緩沖的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_LESS);
};
}
GL_EQUAL
在片段深度值等于緩沖區(qū)的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_EQUAL);
};
}
GL_LEQUAL
在片段深度值小于等于緩沖區(qū)的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_LEQUAL);
};
}
GL_GREATER
在片段深度值大于緩沖區(qū)的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_GREATER);
};
}
GL_NOTEQUAL
在片段深度值不等于緩沖區(qū)的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_NOTEQUAL);
};
}
GL_GEQUAL
在片段深度值大于等于緩沖區(qū)的深度值時(shí)通過測(cè)試
-(void)initSubObject{
//生命周期三秒鐘
[super initSubObject];
glEnable(GL_DEPTH_TEST);
self.glDrawConfig = ^{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};
self.glDrawMaskBeginConfig = ^{
glDepthFunc(GL_ALWAYS);
};
self.glDrawMaskEndConfig = ^{
glDepthFunc(GL_GEQUAL);
};
}
深度值精度
深度緩沖包含了一個(gè)介于0.0和1.0之間的深度值辨赐,它將會(huì)與觀察者視角所看見的場(chǎng)景中所有物體的z值進(jìn)行比較优俘。觀察空間的z值可能是投影平截頭體的近平面(Near)和遠(yuǎn)平面(Far)之間的任何值。我們需要一種方式來將這些觀察空間的z值變換到[0, 1]范圍之間掀序,其中的一種方式就是將它們線性變換到[0, 1]范圍之間帆焕。下面這個(gè)(線性)方程將z值變換到了0.0到1.0之間的深度值:
深度值與z軸成正相關(guān)
這里的near和far值是我們之前提供給投影矩陣設(shè)置可視平截頭體的那個(gè) near 和 far 值
這個(gè)方程需要平截頭體中的一個(gè)z值,并將它變換到了[0, 1]的范圍中不恭。z值和對(duì)應(yīng)的深度值之間的關(guān)系可以在下圖中看到:
然而叶雹,在實(shí)踐中是幾乎永遠(yuǎn)不會(huì)使用這樣的線性深度緩沖(Linear Depth Buffer)的。要想有正確的投影性質(zhì)换吧,需要使用一個(gè)非線性的深度方程折晦,它是與 1/z 成正比的。它做的就是在z值很小的時(shí)候提供非常高的精度沾瓦,而在z值很遠(yuǎn)的時(shí)候提供更少的精度满着。
花時(shí)間想想這個(gè):我們真的需要對(duì)1000單位遠(yuǎn)的深度值和只有1單位遠(yuǎn)的充滿細(xì)節(jié)的物體使用相同的精度嗎?線性方程并不會(huì)考慮這一點(diǎn)贯莺。
由于非線性方程與 1/z 成正比风喇,在1.0和2.0之間的z值將會(huì)變換至1.0到0.5之間的深度值,這就是一個(gè)float提供給我們的一半精度了缕探,這在z值很小的情況下提供了非常大的精度魂莫。在50.0和100.0之間的z值將會(huì)只占2%的float精度,這正是我們所需要的爹耗。這樣的一個(gè)考慮了遠(yuǎn)近距離的方程是這樣的:
其實(shí)就是線性關(guān)系的變種.更接近現(xiàn)實(shí)
如果你不知道這個(gè)方程是怎么回事也不用擔(dān)心耙考。重要的是要記住深度緩沖中的值在屏幕空間中不是線性的(在透視矩陣應(yīng)用之前在觀察空間中是線性的)。深度緩沖中0.5的值并不代表著物體的z值是位于平截頭體的中間了潭兽,這個(gè)頂點(diǎn)的z值實(shí)際上非常接近近平面琳骡!你可以在下圖中看到z值和最終的深度緩沖值之間的非線性關(guān)系:
可以看到,深度值很大一部分是由很小的z值所決定的讼溺,這給了近處的物體很大的深度精度。這個(gè)(從觀察者的視角)變換z值的方程是嵌入在投影矩陣中的最易,所以當(dāng)我們想將一個(gè)頂點(diǎn)坐標(biāo)從觀察空間至裁剪空間的時(shí)候這個(gè)非線性方程就被應(yīng)用了怒坯。
深度緩沖的可視化
我們知道片段著色器中,內(nèi)建gl_FragCoord向量的z值包含了那個(gè)特定片段的深度值藻懒。如果我們將這個(gè)深度值輸出為顏色剔猿,我們可以顯示場(chǎng)景中所有片段的深度值。我們可以根據(jù)片段的深度值返回一個(gè)顏色向量來完成這一工作:
precision mediump float;
varying vec2 vTexCoords;
uniform sampler2D uTexture;
void main()
{
vec4 textureColor = texture2D(uTexture, vTexCoords);
gl_FragColor = vec4(vec3(gl_FragCoord.z), 1.0);;
}
代碼含義:
+ 生成了一個(gè)地面和兩個(gè)正方體.
測(cè)試結(jié)果
這很清楚地展示了深度值的非線性性質(zhì)嬉荆。近處的物體比起遠(yuǎn)處的物體對(duì)深度值有著更大的影響归敬。只需要移動(dòng)幾厘米就能讓顏色從暗完全變白。
如何變成線性關(guān)系呢?
深度沖突
一個(gè)很常見的視覺錯(cuò)誤會(huì)在兩個(gè)平面或者三角形非常緊密地平行排列在一起時(shí)會(huì)發(fā)生,深度緩沖沒有足夠的精度來決定兩個(gè)形狀哪個(gè)在前面汪茧。結(jié)果就是這兩個(gè)形狀不斷地在切換前后順序椅亚,這會(huì)導(dǎo)致很奇怪的花紋。這個(gè)現(xiàn)象叫做深度沖突(Z-fighting)舱污,因?yàn)樗雌饋硐袷沁@兩個(gè)形狀在爭(zhēng)奪(Fight)誰該處于頂端呀舔。
在我們一直使用的場(chǎng)景中,有幾個(gè)地方的深度沖突還是非常明顯的扩灯。箱子被放置在地板的同一高度上媚赖,這也就意味著箱子的底面和地板是共面的(Coplanar)。這兩個(gè)面的深度值都是一樣的珠插,所以深度測(cè)試沒有辦法決定應(yīng)該顯示哪一個(gè)惧磺。
深度沖突是深度緩沖的一個(gè)常見問題,當(dāng)物體在遠(yuǎn)處時(shí)效果會(huì)更明顯(因?yàn)樯疃染彌_在z值比較大的時(shí)候有著更小的精度)捻撑。深度沖突不能夠被完全避免磨隘,但一般會(huì)有一些技巧有助于在你的場(chǎng)景中減輕或者完全避免深度沖突、
防止深度沖突
第一個(gè)也是最重要的技巧是永遠(yuǎn)不要把多個(gè)物體擺得太靠近布讹,以至于它們的一些三角形會(huì)重疊琳拭。通過在兩個(gè)物體之間設(shè)置一個(gè)用戶無法注意到的偏移值,你可以完全避免這兩個(gè)物體之間的深度沖突描验。在箱子和地板的例子中白嘁,我們可以將箱子沿著正y軸稍微移動(dòng)一點(diǎn)。箱子位置的這點(diǎn)微小改變將不太可能被注意到膘流,但它能夠完全減少深度沖突的發(fā)生絮缅。然而,這需要對(duì)每個(gè)物體都手動(dòng)調(diào)整呼股,并且需要進(jìn)行徹底的測(cè)試來保證場(chǎng)景中沒有物體會(huì)產(chǎn)生深度沖突耕魄。
第二個(gè)技巧是盡可能將近平面設(shè)置遠(yuǎn)一些。在前面我們提到了精度在靠近近平面時(shí)是非常高的彭谁,所以如果我們將近平面遠(yuǎn)離觀察者泡仗,我們將會(huì)對(duì)整個(gè)平截頭體有著更大的精度。然而盲链,將近平面設(shè)置太遠(yuǎn)將會(huì)導(dǎo)致近處的物體被裁剪掉妈拌,所以這通常需要實(shí)驗(yàn)和微調(diào)來決定最適合你的場(chǎng)景的近平面距離。
另外一個(gè)很好的技巧是犧牲一些性能狭园,使用更高精度的深度緩沖读处。大部分深度緩沖的精度都是24位的,但現(xiàn)在大部分的顯卡都支持32位的深度緩沖唱矛,這將會(huì)極大地提高精度罚舱。所以井辜,犧牲掉一些性能,你就能獲得更高精度的深度測(cè)試管闷,減少深度沖突粥脚。
我們上面討論的三個(gè)技術(shù)是最普遍也是很容易實(shí)現(xiàn)的抗深度沖突技術(shù)了。還有一些更復(fù)雜的技術(shù)渐北,但它們依然不能完全消除深度沖突阿逃。深度沖突是一個(gè)常見的問題,但如果你組合使用了上面列舉出來的技術(shù)赃蛛,你可能不會(huì)再需要處理深度沖突了恃锉。
測(cè)試代碼 核心代碼
#import "DepthTestViewController.h"
#import "DepthTestBindObject.h"
#import "CubeManager.h"
float planeVertices[] = {
// positions // texture Coords (note we set these higher than 1 (together with GL_REPEAT as texture wrapping mode). this will cause the floor texture to repeat)
5.0f, -0.5f, 5.0f, 2.0f, 0.0f,
-5.0f, -0.5f, 5.0f, 0.0f, 0.0f,
-5.0f, -0.5f, -5.0f, 0.0f, 2.0f,
5.0f, -0.5f, 5.0f, 2.0f, 0.0f,
-5.0f, -0.5f, -5.0f, 0.0f, 2.0f,
5.0f, -0.5f, -5.0f, 2.0f, 2.0f
};
@interface DepthTestViewController ()
@property (nonatomic ,strong) Vertex * vertex;
@property (nonatomic ,strong) Vertex * planeVertex ;
@property (nonatomic ,strong) TextureUnit * cubeUnit ;
@property (nonatomic ,strong) TextureUnit * floorUnit ;
@end
@implementation DepthTestViewController
-(void)initSubObject{
//生命周期三秒鐘
glEnable(GL_DEPTH_TEST);
self.bindObject = [DepthTestBindObject new];
}
-(void)loadVertex{
self.vertex= [Vertex new];
int vertexNum =[CubeManager getTextureNormalVertexNum];
[self.vertex allocVertexNum:vertexNum andEachVertexNum:5];
for (int i=0; i<vertexNum; i++) {
float onevertex[5];
for (int j=0; j<3; j++) {
onevertex[j]=[CubeManager getTextureNormalVertexs][i*8+j];
}
for (int j=0; j<2; j++) {
onevertex[j+3]=[CubeManager getTextureNormalVertexs][i*8+6+j];
}
[self.vertex setVertex:onevertex index:i];
}
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
self.planeVertex= [Vertex new];
vertexNum =6;
[self.planeVertex allocVertexNum:vertexNum andEachVertexNum:5];
for (int i=0; i<vertexNum; i++) {
float onevertex[5];
for (int j=0; j<3; j++) {
onevertex[j]=planeVertices[i*5+j];
}
for (int j=0; j<2; j++) {
onevertex[j+3]=planeVertices[i*5+3+j];
}
[self.planeVertex setVertex:onevertex index:i];
}
[self.planeVertex bindBufferWithUsage:GL_STATIC_DRAW];
}
-(void)createTextureUnit{
self.cubeUnit = [TextureUnit new];
[self.cubeUnit setImage:[UIImage imageNamed:@"marble.jpg"] IntoTextureUnit:GL_TEXTURE0 andConfigTextureUnit:nil];
self.floorUnit = [TextureUnit new];
[self.floorUnit setImage:[UIImage imageNamed:@"metal.png"] IntoTextureUnit:GL_TEXTURE1 andConfigTextureUnit:^{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClearColor(0,0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLKMatrix4 viewMatrix =
GLKMatrix4MakeLookAt(
0.0, 0.0, 3.0, // Eye position
0.0, 0.0, 0.0, // Look-at position
0.0, 1.0, 0.0); // Up direction
glUniformMatrix4fv(self.bindObject->uniforms[DT_uniform_view], 1, 0,viewMatrix.m);
GLfloat aspectRatio= CGRectGetWidth([UIScreen mainScreen].bounds) / CGRectGetHeight([UIScreen mainScreen].bounds);
GLKMatrix4 projectionMatrix =
GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(85.0f),
aspectRatio,
1.0f,
20.0f);
glUniformMatrix4fv(self.bindObject->uniforms[DT_uniform_projection], 1, 0,projectionMatrix.m);
[self drawFloor];
[self drawCubeLocation:GLKVector3Make(-1.0f, 0.0f, -1.0f)];
[self drawCubeLocation:GLKVector3Make(0.0f, 0.0f, 1.0f)];
}
-(void)drawCubeLocation:(GLKVector3)location{
GLKMatrix4 mode = GLKMatrix4Identity;
mode = GLKMatrix4TranslateWithVector3(mode, location);
mode = GLKMatrix4Rotate(mode, 30*M_PI/180.0, 0, 1, 0);
glUniformMatrix4fv(self.bindObject->uniforms[DT_uniform_model], 1, 0,mode.m);
[self.cubeUnit bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DT_uniform_Texture]];
[self.vertex enableVertexInVertexAttrib:DT_aPos numberOfCoordinates:3 attribOffset:0];
[self.vertex enableVertexInVertexAttrib:DT_aTexCoords numberOfCoordinates:2 attribOffset:sizeof(float)*3];
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:[CubeManager getTextureNormalVertexNum]];
}
-(void)drawFloor{
GLKMatrix4 mode = GLKMatrix4Identity;
glUniformMatrix4fv(self.bindObject->uniforms[DT_uniform_model], 1, 0,mode.m);
[self.floorUnit bindtextureUnitLocationAndShaderUniformSamplerLocation:self.bindObject->uniforms[DT_uniform_Texture]];
[self.planeVertex enableVertexInVertexAttrib:DT_aPos numberOfCoordinates:3 attribOffset:0];
[self.planeVertex enableVertexInVertexAttrib:DT_aTexCoords numberOfCoordinates:2 attribOffset:sizeof(float)*3];
[self.planeVertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:6];
}
@end