openGL es 教程翻译4(实现一个旋转的方块)

//
//  OpenGLView.m
//  OpenGLES22
//
//  Created by stephen.xing on 13/6/14.
//  Copyright (c) 2014 IDREAMSKEY. All rights reserved.
//

#import "OpenGLView.h"
#import "CC3GLMatrix.h"


typedef struct {
    float Position[3];
    float Color[4];
} Vertex;

const Vertex Vertices[] = {
    {{1, -1, 0}, {1, 0, 0, 1}},
    {{1, 1, 0}, {0, 1, 0, 1}},
    {{-1, 1, 0}, {0, 0, 1, 1}},
    {{-1, -1, 0}, {0, 0, 0, 1}}
}; // 每个顶点的信息 2.0 可以让我们方便的组织顶点的数据 方式不限了

const GLubyte Indices[] = {
    0, 1, 2,
    2, 3, 0
}; // 数据有了,,下面我们需要把他们传递给openGL


@implementation OpenGLView


- (void)setupDisplayLink {
    // 注册刷新屏幕时候的回调
    // render 函数必须有一个CADisplayLink* 类型的参数
    CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setupLayer];
        [self setupContext];
        [self setupRenderBuffer];
        [self setupFrameBuffer];
        
        [self compileShaders];
        [self setupVBOs];
        
        // [self render];
        [self setupDisplayLink]; // 改掉只渲染一次为 开启一个CADisplayLink的刷新定时器
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/


// 修改view绑定layer的类型
+ (Class)layerClass { // 我们通过重写这个方法就能保证View的layer是一种特殊类型的layer CAEAGLLayer
    return [CAEAGLLayer class];
}

// 是指layer为 不透明
- (void)setupLayer { // 默认情况下CAEAGLLayer是透明的,但是那样影响性能,所以我们设置为不透明
    _eaglLayer = (CAEAGLLayer*) self.layer;
    _eaglLayer.opaque = YES;
}

// 创建 context对象
- (void)setupContext { // context 用于管理iOS使用OpenGl所需要的所有信息
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2; // context是和版本号有关的
    _context = [[EAGLContext alloc] initWithAPI:api];
    if (!_context) {
        NSLog(@"Failed to initialize OpenGLES 2.0 context");
        exit(1);
    }
    
    if (![EAGLContext setCurrentContext:_context]) {
        NSLog(@"Failed to set current OpenGL context");
        exit(1);
    }
}

// create render buffer
- (void)setupRenderBuffer {
    glGenRenderbuffers(1, &_colorRenderBuffer); // 用于产生一个缓冲区对象,该函数为_colorRenderBuffer赋值
    
    // 以后我说GL_RENDERBUFFER指的就_colorRenderBuffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    
    // 为渲染缓冲区非配存储空间,通过context对象的renderbufferStorage函数生成这个对象
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}

// create frame buffer
- (void)setupFrameBuffer {
    GLuint framebuffer; // 帧缓冲区 包含 渲染缓冲区和颜色缓冲区 蒙版缓冲区
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    
    // 将renderbuffer 附着到 framebuffer的GL_COLOR_ATTACHMENT0
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, _colorRenderBuffer);
}


- (GLuint) compileShader:(NSString*) shaderName withType:(GLenum) shaderType{
    
    NSString* shaderFileExt = [[NSString alloc] initWithFormat:@"%@", @"vs"];
    if( shaderType == GL_FRAGMENT_SHADER){
        shaderFileExt  = [[NSString alloc] initWithFormat:@"%@", @"fs" ];
    } // 不同shader 的后缀名不同
    
    // 把shader文件的内存读取出来 保存到shaderString 这个字符串中
    NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName
                                                           ofType:shaderFileExt];
    NSError* error;
    NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath
                                                       encoding:NSUTF8StringEncoding error:&error];
    if (!shaderString) {
        NSLog(@"Error loading shader: %@", error.localizedDescription);
        exit(1);
    }
    
    // 创建一个代表shader的OpenGL对象,传入参数需要说明是 vs 还是 fs
    GLuint shaderHandle = glCreateShader(shaderType);
    
    // 把shader 的内容传递给 OpenGL
    const char * shaderStringUTF8 = [shaderString UTF8String];
    int shaderStringLength = [shaderString length];
    glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
    
    // 让OpenGL为我们编译shader
    glCompileShader(shaderHandle);
    
    // 如果编译失败就输出失败原因--方便我们修改shader,,失败就退出
    GLint compileSuccess;
    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
    
    return shaderHandle; // 成功则返回shader的句柄
    
}

// 编译通过了,我们还需要几个步骤
// 1. 链接shader
// 2. 让OpenGL真正的使用这些shader
// 3. 得到传入参数(attribute)的指针,然后将输入参数传递进去
- (void)compileShaders {
    
    // 分别编译 vertex shader 和 fragment shader
    GLuint vertexShader = [self compileShader:@"SimpleWithoutComment"
                                     withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:@"SimpleWithoutComment"
                                       withType:GL_FRAGMENT_SHADER];
    
    // 链接
    GLuint programHandle = glCreateProgram();
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
    glLinkProgram(programHandle);
    
    // 如果链接失败则输出失败原因 并退出程序
    GLint linkSuccess;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
    
    // 让OpenGL开始使用这个shader
    glUseProgram(programHandle);
    
    // 得到输入参数的指针,以便传值进去
    _positionSlot = glGetAttribLocation(programHandle, "Position");
    _colorSlot = glGetAttribLocation(programHandle, "SourceColor");
    // 使OpenGL 允许我们向输入参数传值
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
    
    _projectionUniform = glGetUniformLocation(programHandle, "Projection");
    // 拿到 投影矩阵的 指针 句柄
    
    _modelViewUniform = glGetUniformLocation(programHandle, "Modelview");
}

// 我们在compileShaders 后面马上调用render 所以用的是同一个program
- (void)render:(CADisplayLink*) displayLink {
    glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0); // 设置清屏色
    glClear(GL_COLOR_BUFFER_BIT);  // 执行真正的清屏动作--只是清空 rendre/color buffer
    
    // 生成投影矩阵
    CC3GLMatrix *projection = [CC3GLMatrix matrix];
    float h = 4.0f * self.frame.size.height / self.frame.size.width;
    [projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:4 andFar:10];
    // openGL默认是向z轴的负方向观察的
    glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
    
    // 生成模型视图矩阵
    CC3GLMatrix *modelView = [CC3GLMatrix matrix];
    // CACurrentMediaTime 得到当前时间,根据这个时间左右移动模型
    [modelView populateFromTranslation:CC3VectorMake(0 /*sin(CACurrentMediaTime())*/, 0, -7)];// 移动我们将会绘制的东西
    // 在移动的基础上添加旋转效果
    _currentRotation += displayLink.duration * 90;
    [modelView rotateBy:CC3VectorMake(_currentRotation, _currentRotation, 0)];
    // 模型视图矩阵传递到openGL
    glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
    
    // 我们在UIView的多大比例中渲染,这里我们全UIView进行渲染,我们也可以指渲染一部分
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    // 把当前的顶点值 传递给shader的输入参数
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), 0);
    // 参数解析:
    // shader输入参数的 句柄 glGetAttribLocation 得到的
    // 每个顶点的Position 参数是一个3元组
    // 3元组的每个元素是float类型
    // 每个顶点的数据结构的 内存大小
    // 顶点结构中 位置信息 距离结构开始的偏移量
    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
                          sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
    
    // 把最终的图形画出来
    glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
                   GL_UNSIGNED_BYTE, 0);
    // 参数解释:
    // 绘制的方式  点 线 三角形
    // 顶点数目
    // 顶点下标数组的数据类型 byte
    //
    
    
    
    
    [_context presentRenderbuffer:GL_RENDERBUFFER]; // render/color buffer 指的是一个buffer
}


// 向OpenGL传值的最好方式 叫做定点缓冲区对象 VBO
// VBO 是OpenGL中为我们存储顶点数据的缓冲区 我们可以通过函数调用将顶点信息从cpu ---> gpu
// VBO 有两种 1保存的是每个顶点的信息 2保存的是顶点的序号信息
- (void)setupVBOs {
    
    GLuint vertexBuffer;
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
    
    GLuint indexBuffer;
    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
    
}

// 默认情况下 OpenGL 是2维的----右上角(1,1) 左下角(-1, -1)
// 如果我们要在2D屏幕上达到 3D的效果 需要使用投影矩阵
// 投影矩阵的主要概念是 近平面和远平面  物体越接近近平面我们把它缩的越小 物体越接近远平面 我们把它放的越大




- (void) dealloc
{
   // arc 不需要 手工释放内存,,编译器替我们干了
}
@end

<pre name="code" class="objc">//
//  OpenGLView.h
//  OpenGLES22
//
//  Created by stephen.xing on 13/6/14.
//  Copyright (c) 2014 IDREAMSKEY. All rights reserved.
//

//#import <UIKit/UIKit.h>
//
//@interface OpenGLView : UIView
//
//@end

#import <UIKit/UIKit.h> 
#import <QuartzCore/QuartzCore.h> 
#include <OpenGLES/ES2/gl.h> 
#include <OpenGLES/ES2/glext.h>   

@interface OpenGLView : UIView
{
    CAEAGLLayer* _eaglLayer;
    EAGLContext* _context;
    GLuint _colorRenderBuffer;
    
    
    GLuint _positionSlot;
    GLuint _colorSlot;
    
    GLuint _projectionUniform;
    GLuint _modelViewUniform;
    
    float _currentRotation;

}
@end


 

@implementation AppDelegate
@synthesize glView = _glView;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
    self.glView = [[OpenGLView alloc] initWithFrame:screenBounds];
    [self.window addSubview:_glView]; // 为window 增加一个子视图
    
    [self.window makeKeyAndVisible];
    return YES;
}

//
//  AppDelegate.h
//  OpenGLES22
//
//  Created by stephen.xing on 13/6/14.
//  Copyright (c) 2014 IDREAMSKEY. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "OpenGLView.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
    OpenGLView* _glView;
}
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) IBOutlet OpenGLView *glView; // 属性

@end
vertex shader 实际使用的时候请去掉中文注释,不然会报错
// attribute 表示这个shader将会有一个输入变量 名字是 Position
// 一会我们将写代码 给Position 变量赋值  该变量表示的是顶点的位置
// 每一次调用vertex shader 这个值都不一样
attribute vec4 Position;

// 另一个输入变量,代表每个顶点的颜色
attribute vec4 SourceColor;

// 也是一种变量, 没有 attribute 修饰所以不是输入变量 而是一个输出变量会传递到 fs
// varying的含义:我会告诉你顶点的颜色,但是如果你想得到顶点以外的像素的颜色 请用附件两个顶点插值得到
varying vec4 DestinationColor;

// Add right before the main
// 我们增加一个输入参数 Projection
// uniform 意味着对于所有的顶点 这个数是一样的
// 每一次调用vertex shader 这个值都一样
// 矩阵可以帮助我们 缩放 旋转 移动 顶点
// Projection 矩阵可以帮助我们 移动顶点
// 对某个顶点实行某个变换只要 左乘一个相关的矩阵就可以了
uniform mat4 Projection;

// 入口
void main(void){
    
    // 顶点的 目标颜色(输出颜色) = 原来的颜色, 让OpenGl为我们插值
    DestinationColor = SourceColor;
    
    // gl_Position 是一个内建的输出参数---决定顶点的最终显示位置
    // 这里就让顶点还在原来的位置不变
    //gl_Position = Position;
    
    // Modify gl_Position line as follows
    gl_Position = Projection * Position;
}
frament shader
// fragmeng shader 的输入参数 lowp 表示 精度
// rule of thumb 经验主义的规则 是使用 尽量低的精度
// lowp medp highp 三个档
varying lowp vec4 DestinationColor;

// 入口
void main(void){
    // gl_FragColor 也是一个内建的输出参数
    // DestinationColor 已经经过了 OpenGL为我们插值---直接把插值的结果作为最终颜色..
    gl_FragColor = DestinationColor;
}

// Xcode 不能编译shader 只是把shader 打包到bundle中
// 我们的app 需要负责编译和运行shader----在app的运行时
// 让app在运行时编译另外一段代码有点奇怪,但OpenGL做成这个颜色可以使我们的shader代码独立于 图形处理器


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值