實(shí)現(xiàn)效果

image.png
實(shí)現(xiàn)目的
本案例主要實(shí)現(xiàn)繪制金字塔,并顯示法向量位置和顏色和光源顏色
源碼分析
首先新建OPenGL ES上下文,然后獲取GLKView,將上下文設(shè)置為當(dāng)前上下文。接下來初始化并設(shè)置燈光的效果和燈光位置,同時(shí)也需要設(shè)置法線的燈光配置。為了更好地觀察金字塔的變化還需要將矩陣旋轉(zhuǎn)一定角度和平移矩陣,然后設(shè)置金字塔的頂點(diǎn)位置,通過結(jié)構(gòu)體來設(shè)置的,最后再分配內(nèi)存位置和大小。
-(void)setUp
{
//1.新建OpenGL ES 上下文
self.mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView *view = (GLKView *)self.view;
view.context = self.mContext;
view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
//將mContext 設(shè)置為當(dāng)前context
[EAGLContext setCurrentContext:self.mContext];
//2.設(shè)置燈光效果
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.light0.enabled = GL_TRUE;
//光的漫射部分
self.baseEffect.light0.diffuseColor = GLKVector4Make(0.7f, 0.7f, 0.7f, 1.0f);
//世界坐標(biāo)中的光的位置
self.baseEffect.light0.position = GLKVector4Make(1.0f, 1.0f, 0.5f, 0.0f);
//設(shè)置法線配置
self.extraEffect = [[GLKBaseEffect alloc] init];
self.extraEffect.useConstantColor = GL_TRUE;
//調(diào)整模型矩陣,更好地觀察
//可以嘗試不執(zhí)行這段代碼,改為false
if (true) {
//圍繞x軸旋轉(zhuǎn)-60度
//返回一個(gè)4*4矩陣進(jìn)行繞行任意矢量旋轉(zhuǎn)
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(-60.0f), 1.0f, 0.0f, 0.0f);
//圍繞Z軸,旋轉(zhuǎn)-30度
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(-30.0f), 0.0f, 0.0f, 1.0f);
//圍繞Z方向,移動(dòng)0.25f
modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, 0.0f, 0.0f, 0.25f);
//設(shè)置baseEffect,extraEffect 模型矩陣
self.baseEffect.transform.modelviewMatrix = modelViewMatrix;
//法線一樣需要修改,因?yàn)槭腔赽aseeffect變化的
self.extraEffect.transform.modelviewMatrix = modelViewMatrix;
}
//設(shè)置清屏顏色
[self setClearColor:GLKVector4Make(0.0f, 0.0f, 0.0f, 1.0f)];
//確定圖形的8個(gè)面
triangles[0] = SceneTriangleMake(vertexA, vertexB, vertexD);
triangles[1] = SceneTriangleMake(vertexB, vertexC, vertexF);
triangles[2] = SceneTriangleMake(vertexD, vertexB, vertexE);
triangles[3] = SceneTriangleMake(vertexE, vertexB, vertexF);
triangles[4] = SceneTriangleMake(vertexD, vertexE, vertexG);
triangles[5] = SceneTriangleMake(vertexE, vertexF, vertexH);
triangles[6] = SceneTriangleMake(vertexG, vertexD, vertexH);
triangles[7] = SceneTriangleMake(vertexH, vertexF, vertexI);
//初始化緩存區(qū)
//頂點(diǎn)緩存區(qū)
/*
參數(shù)1:數(shù)據(jù)大小 3個(gè)GLFloat類型,x,y,z
參數(shù)2:有多少個(gè)數(shù)據(jù),count
參數(shù)3:數(shù)據(jù)大小
參數(shù)4:用途 GL_STATIC_DRAW,
*/
self.vertexBuffer = [[AGLKVertexAttribArrayBuffer alloc] initWithAttribStride:sizeof(SceneVertex) numberOfVertices:sizeof(triangles)/sizeof(SceneVertex) bytes:triangles usage:GL_DYNAMIC_DRAW];
//因?yàn)闀簳r(shí)不知道法線的個(gè)數(shù)和數(shù)據(jù)大小,所以參數(shù)二填寫0,參數(shù)三暫時(shí)填寫NULL
self.extraBuffer = [[AGLKVertexAttribArrayBuffer alloc] initWithAttribStride:sizeof(SceneVertex) numberOfVertices:0 bytes:NULL usage:GL_DYNAMIC_DRAW];
//中心點(diǎn)的高
self.centexVertexHeight = 0.0f;
//是否使用面法線
self.shouldUseFaceNormals = YES;
}
- (void)setClearColor:(GLKVector4)clearColorRGBA
{
glClearColor(clearColorRGBA.r,
clearColorRGBA.g,
clearColorRGBA.b,
clearColorRGBA.a);
}
接下來開始繪制頂點(diǎn)數(shù)據(jù)并且判斷是否需要繪制法線數(shù)據(jù)
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
//設(shè)置清屏顏色
glClearColor(0.3f, 0.3f, 0.3f, 0.3f);
glClear(GL_COLOR_BUFFER_BIT);
//準(zhǔn)備繪制
[self.baseEffect prepareToDraw];
//準(zhǔn)備繪制頂點(diǎn)數(shù)據(jù)
/*
其實(shí)就是把數(shù)據(jù)傳遞過去,然后指定讀取方式
參數(shù)1:數(shù)據(jù)是做什么用的
參數(shù)2:數(shù)據(jù)讀取個(gè)數(shù)
參數(shù)3:數(shù)據(jù)讀取索引
參數(shù)4:是否調(diào)用glEnableVertexAttribArray
著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
默認(rèn)情況下,出于性能考慮,所有頂點(diǎn)著色器的屬性(Attribute)變量都是關(guān)閉的,意味著數(shù)據(jù)在著色器端是不可見的,哪怕數(shù)據(jù)已經(jīng)上傳到GPU,由glEnableVertexAttribArray啟用指定屬性,才可在頂點(diǎn)著色器中訪問逐頂點(diǎn)的屬性數(shù)據(jù)。glVertexAttribPointer或VBO只是建立CPU和GPU之間的邏輯連接,從而實(shí)現(xiàn)了CPU數(shù)據(jù)上傳至GPU。但是,數(shù)據(jù)在GPU端是否可見,即,著色器能否讀取到數(shù)據(jù),由是否啟用了對(duì)應(yīng)的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點(diǎn)著色器讀取GPU(服務(wù)器端)數(shù)據(jù)。
那么,glEnableVertexAttribArray應(yīng)該在glVertexAttribPointer之前還是之后調(diào)用?答案是都可以,只要在繪圖調(diào)用(glDraw*系列函數(shù))前調(diào)用即可。
*/
[self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:offsetof(SceneVertex, position) shouldEnable:YES];
//準(zhǔn)備繪制光照數(shù)據(jù)
[self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribNormal numberOfCoordinates:3 attribOffset:offsetof(SceneVertex, normal) shouldEnable:YES];
[self.vertexBuffer drawArrayWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:sizeof(triangles)/sizeof(SceneVertex)];
//是否需要繪制光照法線
if (self.shouldDrawNormals) {
[self drawNormals];
}
}
如果需要繪制法線數(shù)據(jù)的話,那么我們開始繪制法線數(shù)據(jù)
//繪制法線
-(void)drawNormals
{ //1.聲明繪制光照法線數(shù)組
GLKVector3 normalLineVertices[NUM_LINE_VERTS];
//2.以每個(gè)頂點(diǎn)的坐標(biāo)為起點(diǎn),頂點(diǎn)坐標(biāo)加上法向量的偏移值作為終點(diǎn),更新法線顯示數(shù)組
//參數(shù)1.三角形數(shù)組
//參數(shù)2:光源位置
//參數(shù)3:法線顯示的頂點(diǎn)數(shù)組
SceneTrianglesNormalLinesUpdate(triangles, GLKVector3MakeWithArray(self.baseEffect.light0.position.v), normalLineVertices);
//為extraBuffer 重新開辟空間
[self.extraBuffer reinitWithAttribStride:sizeof(GLKVector3) numberOfVertices:NUM_LINE_VERTS bytes:normalLineVertices];
//準(zhǔn)備繪制數(shù)據(jù)
[self.extraBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:0 shouldEnable:YES];
/*
指示是否使用常量顏色的布爾值。
如果該值設(shè)置為gl_true,然后存儲(chǔ)在設(shè)置屬性的值為每個(gè)頂點(diǎn)的顏色值。如果該值設(shè)置為gl_false,那么你的應(yīng)用將使glkvertexattribcolor屬性提供每頂點(diǎn)顏色數(shù)據(jù)。默認(rèn)值是gl_false。
*/
self.extraEffect.useConstantColor = GL_TRUE;
//設(shè)置光源顏色為綠色,畫頂點(diǎn)發(fā)現(xiàn)
self.extraEffect.constantColor = GLKVector4Make(0.0f, 1.0f, 0.0f, 1.0f);
//準(zhǔn)備繪制-綠色的法線
[self.extraEffect prepareToDraw];
//繪制線段
[self.extraBuffer drawArrayWithMode:GL_LINES startVertexIndex:0 numberOfVertices:NUM_NORMAL_LINE_VERTS];
//設(shè)置光源顏色為黃色,并且畫光源
//red+green = yellow
self.extraEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 0.0f, 1.0f);
//準(zhǔn)備繪制-黃色的光源方向線
[self.extraEffect prepareToDraw];
//(NUM_LINE_VERTS - NUM_NORMAL_LINE_VERTS) = 2 .2點(diǎn)確定一條線,繪制最后兩條光源線
[self.extraBuffer drawArrayWithMode:GL_LINES startVertexIndex:NUM_NORMAL_LINE_VERTS numberOfVertices:(NUM_LINE_VERTS - NUM_NORMAL_LINE_VERTS)];
}
設(shè)置centexVertexHeight中心點(diǎn)的高度,可以改變金字塔的頂點(diǎn)和更改法線照射位置,金字塔頂點(diǎn)位置為E點(diǎn),當(dāng)修改E點(diǎn)的時(shí)候,也需要修改與它相關(guān)聯(lián)的頂點(diǎn)
-(void)setCentexVertexHeight:(GLfloat)centexVertexHeight
{
_centexVertexHeight = centexVertexHeight;
//更新頂點(diǎn)E
SceneVertex newVertexE = vertexE;
newVertexE.position.z = _centexVertexHeight;
//同時(shí)需要更新與頂點(diǎn)E相關(guān)的頂點(diǎn),不然無效
triangles[2] = SceneTriangleMake(vertexD, vertexB, newVertexE);
triangles[3] = SceneTriangleMake(newVertexE, vertexB, vertexF);
triangles[4] = SceneTriangleMake(vertexD, newVertexE, vertexH);
triangles[5] = SceneTriangleMake(newVertexE, vertexF, vertexH);
//然后更新法線
[self updateNormals];
}
當(dāng)更改中心點(diǎn)之后,需要更新法向量
//更新法向量
-(void)updateNormals
{
if (self.shouldUseFaceNormals) {
//更新每個(gè)點(diǎn)的平面法向量
SceneTrianglesUpdateFaceNormals(triangles);
}else {
//通過平均值求出每一個(gè)點(diǎn)的法向量
SceneTrianglesUpdateVertexNormals(triangles);
}
//重新渲染
[self.vertexBuffer reinitWithAttribStride:sizeof(SceneVertex) numberOfVertices:sizeof(triangles)/sizeof(SceneVertex) bytes:triangles];
}
當(dāng)開啟繪制法向量的時(shí)候,也需要更新法向量的位置和數(shù)據(jù)
-(void)setShouldUseFaceNormals:(BOOL)shouldUseFaceNormals
{
if (shouldUseFaceNormals != _shouldUseFaceNormals) {
_shouldUseFaceNormals = shouldUseFaceNormals;
[self updateNormals];
}
}
到這里基本代碼已經(jīng)完成。
源碼GitHub代碼