OpenGLES入門 6 -- 全景

步驟

1、初始化上下文;
2、設置緩沖區
3、設置著色器
4、創建圖片紋理
5、初始化參數、手勢、陀螺儀等
6、繪制

1、初始化

_eaglContext =[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:_eaglContext];

_glLayer = (CAEAGLLayer*) self.layer;
// CALayer 默認是透明的,必須將它設為不透明才能讓其可見
_glLayer.opaque = YES;
// 設置描繪屬性,在這里設置不維持渲染內容以及顏色格式為 RGBA8
_glLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];

kEAGLColorFormatRGBA8:使用8位來保存RGBA的值;
kEAGLDrawablePropertyRetainedBacking:設置NO不保留之前繪制的圖像以用來重用;

2、綁定渲染緩沖及幀緩沖區

glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_glLayer];
    
glGenFramebuffers(1,&_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER,_frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _frameBuffer);

渲染緩存:存儲繪制結果的緩沖區
幀緩存:接收渲染結果的緩沖區,為GPU指定存儲渲染結果的區域。

3、設置著色器

//shader
GLuint vertext  =[self compileWithShaderName:@"Vertex" shaderType:GL_VERTEX_SHADER];
GLuint fragment =[self compileWithShaderName:@"Fragment" shaderType:GL_FRAGMENT_SHADER];
    
_glProgram =glCreateProgram();
glAttachShader(_glProgram, vertext);
glAttachShader(_glProgram, fragment);

//操作產生最后的可執行程序,它包含最后可以在硬件上執行的硬件指令。
glLinkProgram(_glProgram);
    
GLint linkSuccess = GL_TRUE;
glGetProgramiv(_glProgram, GL_LINK_STATUS,&linkSuccess);
if (linkSuccess ==GL_FALSE) {
     GLchar glMessage[256];
     glGetProgramInfoLog(_glProgram, sizeof(glMessage), 0, &glMessage[0]);
     NSString *messageString = [NSString stringWithUTF8String:glMessage];
     NSLog(@"program error %@", messageString);
     exit(1);
}
    
//綁定著色器參數
glUseProgram(_glProgram);
_glPosition = glGetAttribLocation(_glProgram,"Position");
-(GLuint)compileWithShaderName:(NSString*)name shaderType:(GLenum)shaderType
{
    //獲取著色器文件
    NSString *shaderPath =[[NSBundle mainBundle]pathForResource:name ofType:@"glsl"];
    NSError *error;
    NSString *strShader =[NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
    NSLog(@"strShader %@",strShader);
    if (!strShader) {
        NSLog(@"shader error %@",error.localizedDescription);
        exit(1);
    }
    
    // 2 創建一個代表shader的OpenGL對象, 指定vertex或fragment shader
    GLuint shaderHandler = glCreateShader(shaderType);
    
    // 3 獲取shader的source
    const char* shaderString = [strShader UTF8String];
    int shaderStringLength = (int)[strShader length];
    glShaderSource(shaderHandler, 1, &shaderString, &shaderStringLength);
    
    // 4 編譯shader
    glCompileShader(shaderHandler);
    
    // 5 查詢shader對象的信息
    GLint compileSuccess;
    glGetShaderiv(shaderHandler, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shaderHandler, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
    return shaderHandler;
}

著色器: 分為Vertex Shader 和Fragment Shader

  • 頂點著色器(Vertex Shader):用于確定圖形形狀
attribute vec4 Position;
attribute vec2 TextureCoords;
varying   vec2 TextureCoordsFrag;

uniform mat4 Matrix;

void main(void)
{
    gl_Position = Matrix * vec4(Position.x,Position.y,Position.z,1.0);
    TextureCoordsFrag = TextureCoords;
}
  • 片段著色器(Fragment Shader):用于確定圖像繪制渲染的顏色
precision mediump float;
uniform sampler2D Texture;
varying vec2 TextureCoordsFrag;

void main(void)
{
    vec4 mask = texture2D(Texture, TextureCoordsFrag);
    gl_FragColor = vec4(mask.rgb,1.0);

}

這里推薦一個介紹GLSL語言的博客,講的還是比較詳細的

4、創建圖片紋理

/**
 * 創建圖片紋理
 */
-(void)initImageTexture
{
   //獲取圖片
    NSString *imgPath =[[NSBundle mainBundle]pathForResource:@"balitieta" ofType:@"jpg"];
    NSData   *data    =[[NSData alloc]initWithContentsOfFile:imgPath];
    UIImage  *image   =[UIImage imageWithData:data];
    _textureID =[self createTextureWithImage:image];
}
-(GLuint)createTextureWithImage:(UIImage*)image
{
    //獲取圖片基本參數
    CGImageRef imageRef =[image CGImage];
    GLuint width   = (GLuint)CGImageGetWidth(imageRef);
    GLuint height  = (GLuint)CGImageGetHeight(imageRef);
    CGRect rect    = CGRectMake(0,0,width,height);
    
    //繪制
    CGColorSpaceRef  colorSpace =  CGColorSpaceCreateDeviceRGB();
    void *imageData  =  malloc(width*height*4);
 
   /**
     *  CGBitmapContextCreate(void * __nullable data,size_t width, size_t height, size_t
     *  bitsPerComponent, size_t bytesPerRow,CGColorSpaceRef cg_nullable space, uint32_t
     *  bitmapInfo)
     *  data:指向繪圖操作被渲染的內存區域,這個內存區域大小應該為(bytesPerRow*height)個字節。如果對繪制操作被
     渲染的內存區域并無特別的要求,那么可以傳遞NULL給參數data。
     *  width:代表被渲染內存區域的寬度。
     *  height:代表被渲染內存區域的高度。
     *  bitsPerComponent:被渲染內存區域中組件在屏幕每個像素點上需要使用的bits位,舉例來說,如果使用32-bit像素和
     RGB顏色格式,那么RGBA顏色格式中每個組件在屏幕每個像素點上需要使用的bits位就為32/4=8。
     *  bytesPerRow:代表被渲染內存區域中每行所使用的bytes位數。
     *  colorspace:用于被渲染內存區域的“位圖上下文”。
     *  bitmapInfo:指定被渲染內存區域的“視圖”是否包含一個alpha(透視)通道以及每個像素相應的位置,除此之外還
     可以指定組件式是浮點值還是整數值。
     */
    CGContextRef contextRef = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    /**
     *  void CGContextTranslateCTM ( CGContextRef c, CGFloat tx, CGFloat ty ):平移坐標系統。
     *  該方法相當于把原來位于 (0, 0) 位置的坐標原點平移到 (tx, ty) 點。在平移后的坐標系統上繪制圖形時,所有坐標點的 X 坐標都相當于增加了 tx,所有點的 Y 坐標都相當于增加了 ty。
     */
    CGContextTranslateCTM(contextRef, 0, height);

    /**
     *  void CGContextScaleCTM ( CGContextRef c, CGFloat sx, CGFloat sy ):縮放坐標系統。
     *  該方法控制坐標系統水平方向上縮放 sx,垂直方向上縮放 sy。在縮放后的坐標系統上繪制圖形時,所有點的 X 坐標都相當于乘以 sx 因子,所有點的 Y 坐標都相當于乘以 sy 因子。
     */
    
    CGContextScaleCTM(contextRef, 1.0f, -1.0f);
    CGColorSpaceRelease(colorSpace);
    CGContextClearRect(contextRef, rect);
    CGContextDrawImage(contextRef, rect, imageRef);
    
    //生成紋理
    glEnable(GL_TEXTURE_2D);
    GLuint textureID;
    glGenTextures(1,&textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    
    //紋理設置
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    
    /**
     *  void glTexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei
     height,GLint border,GLenum format,GLenum type,const GLvoid * pixels);
     *  target  指定目標紋理,這個值必須是GL_TEXTURE_2D。
     *  level   執行細節級別。0是最基本的圖像級別,你表示第N級貼圖細化級別。
     *  internalformat     指定紋理中的顏色組件,這個取值和后面的format取值必須相同。可選的值有
        GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE,GL_LUMINANCE_ALPHA 等幾種。
     *  width   指定紋理圖像的寬度,必須是2的n次方。紋理圖片至少要支持64個材質元素的寬度
     *  height  指定紋理圖像的高度,必須是2的m次方。紋理圖片至少要支持64個材質元素的高度
     *  border  指定邊框的寬度。必須為0。
     *  format  像素數據的顏色格式,必須和internalformatt取值必須相同。可選的值有
     GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE,GL_LUMINANCE_ALPHA 等幾種。
     *  type    指定像素數據的數據類型。可以使用的值有
        GL_UNSIGNED_BYTE,
        GL_UNSIGNED_SHORT_5_6_5,
        GL_UNSIGNED_SHORT_4_4_4_4,
        GL_UNSIGNED_SHORT_5_5_5_1
     *  pixels  指定內存中指向圖像數據的指針
     */
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
   
    //綁定紋理位置
    glBindTexture(GL_TEXTURE_2D, 0);
    //釋放內存
    CGContextRelease(contextRef);
    free(imageData);
    
    return textureID;
}

5、初始化參數、手勢、陀螺儀等

rotateY =   0;
rotateX =   0;

//坐標、紋理、索引
_numIndices = createSphere(200, 1.0, &(_vertexData), &(_texCoords), &_indices, &_numVetex);

//參數
_glPosition     = glGetAttribLocation(_glProgram,"Position");
_textureCoords  = glGetAttribLocation(_glProgram, "TextureCoords");
_texture        = glGetUniformLocation(_glProgram, "Texture");//frag
_uMatrix        = glGetUniformLocation(_glProgram, "Matrix");
-(void)initGesture
{
    //拖動
    UIPanGestureRecognizer *panGesture =[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGesture:)];
    [self addGestureRecognizer:panGesture];
}

-(void)panGesture:(UIPanGestureRecognizer*)gesture
{
    CGPoint translatedPoint = [gesture translationInView:self];
    
    if (gesture.state ==UIGestureRecognizerStateChanged) {
        rotateX+=translatedPoint.y;
        rotateY+=translatedPoint.x;
    }
}
-(void)cmmotion
{
    //創建運動管理者對象
    CMMotionManager *motionManager = [[CMMotionManager alloc]init];
    if (!motionManager.gyroAvailable) { //判斷陀螺儀是否可用
        return;
    }
    
    motionManager.gyroUpdateInterval = 1/60; // 1秒鐘采樣10次
    [motionManager startDeviceMotionUpdates];
   
    self.motionManager =motionManager;
    
}

6、繪制

-(void)draw{
    //清屏
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glViewport(0,0,_viewWidth,_viewHeight);
    
    //激活
    glActiveTexture(GL_TEXTURE5); //指定紋理單元GL_TEXTURE5
    glBindTexture(GL_TEXTURE_2D, _textureID); //綁定,即可從_textureID中取出圖像數據。
    glUniform1i(_texture, 5); // 與紋理單元的序號對應
    
    //render
    [self renderSphereVertice];
    
    // 使用完之后解綁GL_TEXTURE_2D
    glBindTexture(GL_TEXTURE_2D, 0);
    [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
}
-(void)renderSphereVertice
{
    //透視投影
    float aspect = fabsf((float)_viewWidth /(float)_viewHeight);

    GLKMatrix4 perspectiveMatrix =GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0f),aspect, 0.1f,100.0f);
    perspectiveMatrix  = GLKMatrix4Scale(perspectiveMatrix, 1.0, 1.0, 1.0);
    perspectiveMatrix  = GLKMatrix4RotateX(perspectiveMatrix, GLKMathDegreesToRadians(0.05*rotateX));
   
    if (_motionManager.deviceMotion !=nil) {
        //陀螺儀
        double w = _motionManager.deviceMotion.attitude.quaternion.w;
        double x = _motionManager.deviceMotion.attitude.quaternion.x;
        double y = _motionManager.deviceMotion.attitude.quaternion.y;
        double z = _motionManager.deviceMotion.attitude.quaternion.z;
        
        GLKQuaternion quaternion         = GLKQuaternionMake(x,-y, z, w);
        GLKMatrix4    quaternionMatrix4  = GLKMatrix4MakeWithQuaternion(quaternion);
        perspectiveMatrix = GLKMatrix4Multiply(perspectiveMatrix, quaternionMatrix4);
        
        //設置手機觀看視角為從上往下
        perspectiveMatrix = GLKMatrix4RotateX(perspectiveMatrix,-M_PI_2);
    }
    
    //顯示圖像在相機視角有翻轉,所以為-
    perspectiveMatrix  = GLKMatrix4RotateY(perspectiveMatrix, GLKMathDegreesToRadians(-0.05*rotateY));

    //相機視角
    GLKMatrix4 cameraMatrix = GLKMatrix4MakeLookAt(0.0f, 0.0f, 0.0f,
                                                   0.0f, 0.0f, 1.0f,
                                                   0.0f, -1.0f, 0.0f);
    //模型
    GLKMatrix4 modeMatrix = GLKMatrix4Identity;
   
    //MVP
    GLKMatrix4 MVP = GLKMatrix4Identity;
    MVP = GLKMatrix4Multiply(MVP, modeMatrix);
    MVP = GLKMatrix4Multiply(MVP, cameraMatrix);
    MVP = GLKMatrix4Multiply(MVP, perspectiveMatrix);
   
    //矩陣
    glUniformMatrix4fv(_uMatrix, 1, 0, (float*)&MVP);

    // 加載頂點坐標數據
    glGenBuffers(1, &_vertexBuffer); // 申請內存
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); // 將命名的緩沖對象綁定到指定的類型上去
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*_numVetex*3,_vertexData, GL_STATIC_DRAW);
    
    // 加載頂點索引數據
    glGenBuffers(1, &_indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices*sizeof(GLushort), _indices, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(_glPosition);  // 綁定到位置上
    glVertexAttribPointer(_glPosition, 3, GL_FLOAT, GL_FALSE, 3*sizeof(GLfloat), NULL);
  
    // 加載紋理坐標
    glGenBuffers(1, &_texCoordsBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _texCoordsBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*_numVetex*2, _texCoords, GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(_textureCoords);
    glVertexAttribPointer(_textureCoords, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), NULL);
    
    //draw
    glDrawElements(GL_TRIANGLES, (GLsizei)_numIndices,GL_UNSIGNED_SHORT, nil);

    glDeleteBuffers(1, &_vertexBuffer);
    glDeleteBuffers(1, &_indexBuffer);
    glDeleteBuffers(1, &_texCoordsBuffer);
}

下面是效果圖 :


全景.gif

里面球體坐標計算及圖片從EvenCoder獲取,感謝,有興趣的也可以去看下大神寫的swift版本的全景效果!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容