OpenGL ES學(xué)習(xí)之路(8.1) 光照案例

實(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代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容