iOS開發(fā)-OpenGL ES魔方應(yīng)用

分享

這系列收集OpenGL ES的應(yīng)用。

這篇介紹的3D魔方(原文地址),重點(diǎn)是魔方的旋轉(zhuǎn)點(diǎn)擊的判斷

效果展示

概念準(zhǔn)備

拾取
把地形的位置坐標(biāo)編碼到片元的顏色分量中,用戶觸摸時,檢查特定的像素的顏色分量以確定觸摸到的地形的位置。用戶看不到用于拾取的渲染,因?yàn)橛糜谑叭〉南袼仡伾秩揪彺娌粫@示到屏幕上,而是渲染到一個OpenGL ES的幀緩存對象(FBO)中。

  • 1、基于顏色拾取
    把位置信息編碼進(jìn)顏色分量,使用 glReadPixels() 讀取。

把渲染值從FBO讀取到CPU控制的內(nèi)存需要花費(fèi)時間執(zhí)行耗時的同步操作。
拾取在每秒中可能發(fā)生多次,會影響渲染。

  • 2、幾何拾取
    設(shè)想一個光線從平截體近平面上一個觸摸位置頭投射向這個位置對應(yīng)的遠(yuǎn)平面的點(diǎn)。被這個光線穿過的離視點(diǎn)最近的對象就是要拾取的對象。

不需要讀取FBO的渲染值,通過觸摸的視口坐標(biāo)和平截體,可形成光線。

核心思路

魔方直接渲染到屏幕,拾取的時候再渲染一次到FBO,通過拾取結(jié)果決定是旋轉(zhuǎn)某一列還是旋轉(zhuǎn)整個魔方。

具體解析

1、坐標(biāo)系與旋轉(zhuǎn)

#define ROTATE_NONE            -1
#define ROTATE_ALL              0
#define ROTATE_X_CLOCKWISE      1
#define ROTATE_X_ANTICLOCKWISE  2
#define ROTATE_Y_CLOCKWISE      3
#define ROTATE_Y_ANTICLOCKWISE  4
#define ROTATE_Z_CLOCKWISE      5
#define ROTATE_Z_ANTICLOCKWISE  6

ROTATE_NONE 為未旋轉(zhuǎn)
ROTATE_ALL 為旋轉(zhuǎn)整個魔方
ROTATE_X_CLOCKWISE 為繞X軸順時針
ROTATE_X_ANTICLOCKWISE 為繞X軸逆時針
ROTATE_Y_CLOCKWISE 為繞Y軸順時針
ROTATE_Y_ANTICLOCKWISE 為Y軸逆時針
ROTATE_Z_CLOCKWISE 為繞Z軸順時針
ROTATE_Z_ANTICLOCKWISE 為繞Z軸逆時針

魔方的坐標(biāo)系如下:


2、attribute屬性、uniform變量的統(tǒng)一管理

YHCOpenGLProgram是對GLProgram的封裝,可以設(shè)置頂點(diǎn)、片元著色器,設(shè)置attribute屬性、uniform變量。
流程大致分三步

    //1、初始化,并設(shè)置屬性
        _program = [[YHCOpenGLProgram alloc] init];
        [_program setVertexShader:@"Shader"];
        [_program setFragmentShader:@"Shader"];
        [_program addAttributeLocation:@"ATTRIBUTE_VERTEX" forAttribute:@"position"];
        [_program compileAndLink];
        attributes[ATTRIBUTE_VERTEX] = [_program getAttributeIDForIndex:@"ATTRIBUTE_VERTEX"];
     
  
    //2、在鏈接之前 綁定 attribute (attribute location 綁定 必須在鏈接之前)
    for (NSString *key in _attributes) {
        YHCShaderAttribute *thisAttribute = [_attributes objectForKey:key];
        glBindAttribLocation(_programId, thisAttribute.attributeId, [thisAttribute.attributeName UTF8String]);
    }

    //3、鏈接成功后,從 OpenGL 獲取uniform locations (該操作應(yīng)該在link之后進(jìn)行)
    for (NSString *key in _uniforms) {
        YHCShaderUniform *thisUniform = [_uniforms objectForKey:key];
        [thisUniform setUniformLocation:glGetUniformLocation(_programId, [thisUniform.uniformName UTF8String])];
    }

3、邏輯設(shè)計

沒有深入了解,參考原文,還有代碼里的GameLogic類。

4、文字顯示

加載一張含有多個文字的圖片,通過在上面選定區(qū)域來顯示文字(無法顯示中文)。


思考1:是否存在替代的做法?

5、旋轉(zhuǎn)部分魔方的動畫實(shí)現(xiàn)

不斷增大_sliceRotateAngle,當(dāng)_sliceRotateAngle>=90°之后,設(shè)置為_rotationState為ROTATE_NONE,并設(shè)置_currentSlice數(shù)組為-1,完成一次旋轉(zhuǎn)。

        if(_rotationState == ROTATE_X_CLOCKWISE || _rotationState == ROTATE_Y_CLOCKWISE || _rotationState == ROTATE_Z_CLOCKWISE){
            _sliceRotateAngle += rotationAngle;
        }else if(_rotationState == ROTATE_X_ANTICLOCKWISE || _rotationState == ROTATE_Y_ANTICLOCKWISE || _rotationState == ROTATE_Z_ANTICLOCKWISE){
            _sliceRotateAngle += -rotationAngle;
        }else{
            _sliceRotateAngle = 0;
        }

6、旋轉(zhuǎn)整個魔方

監(jiān)聽touchesMove:withEvent:方法,通過locationWithUITouch:View得出點(diǎn)擊位置的Point,和touchesBegan開始記錄的_lastTouchPosition相比,得出繞X、Y軸旋轉(zhuǎn)的角度大小,直接對整個魔方的旋轉(zhuǎn)矩陣進(jìn)行操作。
當(dāng)初始點(diǎn)擊處不在魔方時,旋轉(zhuǎn)整個魔方。根據(jù)點(diǎn)擊初始點(diǎn)的x、y移動的距離,來決定饒Y、X軸的角度,注意是相反的。

- (void)rotateCubeAroundX:(float)x andY:(float)y
{
    GLfloat totalXRotation = x * M_PI / 180.0f;
    GLfloat totalYRotation = y * M_PI / 180.0f;
    if (_rotationState == ROTATE_ALL) {
        [MatrixTools applyRotation:_rotationMatrix x:totalXRotation y:totalYRotation z:0.0];
    }
}

7、旋轉(zhuǎn)部分魔方判斷

獲取點(diǎn)擊的位置,重繪魔方到FBO,獲取點(diǎn)擊位置對應(yīng)的顏色,確定rotationState。

            if (_isSelectMode) {
                glVertexAttribPointer(ATTRIBUTE_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, cubes[i]._colors);
                glEnableVertexAttribArray(ATTRIBUTE_COLOR); // 如果是選擇模式,用顏色
            }else{
                glVertexAttribPointer(ATTRIBUTE_TEXTURE_COORD, 2, GL_FLOAT, 0, 0, cubes[i]._textureCoords);
                glEnableVertexAttribArray(ATTRIBUTE_TEXTURE_COORD); // 如果不上選擇模式,使用紋理坐標(biāo)
            }
    glReadPixels(point1.x,viewport[3]-point1.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor);

思考2:為何不直接讀取屏幕的顏色?

總結(jié)

魔方的邏輯較復(fù)雜,著重了解魔方的顯示、旋轉(zhuǎn),點(diǎn)擊的拾取與判斷。
代碼地址在這里。

思考

1、替代的做法:文字直接添加到UILabel,UILabel繪制成紋理,再加載到OpenGL ES。
2、如果添加的是紋理,顏色變量無法攜帶位置信息。

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

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

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