在此需要GLSL的協助,來制作Vertex及Fragment 的shader,而載入shader則由GLESUtils Class來呼叫。
另外要注意 glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
定義頂點的屬性資料(之後便可設定 vertex shader 的 attributes 變數之值:
// Position, SourceColor)
//
// a. 當在繪製時, OpenGL 會從儲存在緩衝區物件裡的陣列取得頂點資料.
// b. 我們需要告訴 OpenGL 儲存在緩衝區物件裡的頂點陣列資料是什麼格式.
// c. 亦即, 我們需要告訴 OpenGL 如何去理解儲存在緩衝區裡的陣列資料.
// d. 當 glVertexAttribPointer 函式被呼叫時, 它會參照到此時任何綁定到
// GL_ARRAY_BUFFER 的緩衝區, 在此處為 setupVBOs 函式中的 vertexBuffer.
//
// void glVertexAttribPointer(GLuint index,
// GLint size,
// GLenum type,
// GLboolean normalized,
// GLsizei stride,
// const GLvoid * pointer);
//
// 參數說明
// index: 即將被設定的 vertex shader 的 attribute 之索引位置,
// 之前已在 compileShaders 函式中呼叫 glGetAttribLocation 取得.
//
// size: 每個頂點屬性的元素數量, 在此為:位置 3 個(x,y,z); 顏色 4 個(r,g,b,a).
//
// type: 每個元素的資料型別, 在此, 位置與顏色皆為 GL_FLOAT.
//
// normalized: 是否要對頂點進行正規化處理, 在此為否: GL_FALSE
//
// stride(跨度): 二個連續頂點屬性之間的位元偏移量.
// 亦即, 包含個別頂點資料的資料結構之大小, 在此皆為: sizeof(Vertex)
//
// pointer: 在儲存該屬性的資料結構中, 此屬性資料的起始偏移量.
//
// typedef struct {
// float Position[3];
// float Color[4];
// } Vertex;
//
// 說明:
// a. Position 資料是位於 struct 的起始處, 因此偏移量為: 0.
// b. Color 資料是位於 Position 資料之後, 因此偏移量為: sizeof(float) * 3.
原始參考實例 http://blog.csdn.net/kesalin/article/details/8223649
接續前一個專案。
1. 首先會先加入一個GLESUtils的Class檔,繼承自NSObject,主要目的在載入shader的檔案。
GLESUtils.h檔如下
#import <Foundation/Foundation.h>
#include <OpenGLES/ES2/gl.h>
@interface GLESUtils : NSObject
// Create a shader object, load the shader source string, and compile the shader.
//
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString;
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath;
//
///
/// Load a vertex and fragment shader, create a program object, link program.
/// Errors output to log.
/// vertexShaderFilepath Vertex shader source file path.
/// fragmentShaderFilepath Fragment shader source file path
/// return A new program object linked with the vertex/fragment shader pair, 0 on failure
//
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath;
@end
2. GLESUtils.m檔如下
#import "GLESUtils.h"
@implementation GLESUtils
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath
{
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderFilepath
encoding:NSUTF8StringEncoding
error:&error];
if (!shaderString) {
NSLog(@"Error: loading shader file: %@ %@", shaderFilepath, error.localizedDescription);
return 0;
}
return [self loadShader:type withString:shaderString];
}
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString
{
// Create the shader object
GLuint shader = glCreateShader(type);
if (shader == 0) {
NSLog(@"Error: failed to create shader.");
return 0;
}
// Load the shader source
const char * shaderStringUTF8 = [shaderString UTF8String];
glShaderSource(shader, 1, &shaderStringUTF8, NULL);
// Compile the shader
glCompileShader(shader);
// Check the compile status
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if (infoLen > 1) {
char * infoLog = malloc(sizeof(char) * infoLen);
glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
NSLog(@"Error compiling shader:\n%s\n", infoLog );
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath
{
// Load the vertex/fragment shaders
GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER
withFilepath:vertexShaderFilepath];
if (vertexShader == 0)
return 0;
// pixel shader
GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER
withFilepath:fragmentShaderFilepath];
if (fragmentShader == 0) {
glDeleteShader(vertexShader);
return 0;
}
// Create the program object
GLuint programHandle = glCreateProgram();
if (programHandle == 0)
return 0;
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
// Link the program
glLinkProgram(programHandle);
// Check the link status
GLint linked;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1){
char * infoLog = malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(programHandle, infoLen, NULL, infoLog);
NSLog(@"Error linking program:\n%s\n", infoLog);
free(infoLog);
}
glDeleteProgram(programHandle );
return 0;
}
// Free up no longer needed shader resources
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return programHandle;
}
@end
3. 對應的vertex shader檔-->VertexShader.glsl
attribute vec4 vPosition;
void main(void)
{
gl_Position = vPosition;
}
4. 對應的Fragment shader檔 --> FragmentShader.glsl
precision mediump float;
void main()
{
//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
}
5. 在OpenGLView.h加入兩個變數
#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 _frameBuffer;
GLuint _programHandle;
GLuint _positionSlot;
}
- (void)setupLayer;
- (void)setupContext;
- (void)setupRenderBuffer;
- (void)destoryRenderAndFrameBuffer;
- (void)render;
@end
6. 在OpenGLView.m加入所需程式碼
#import "OpenGLView.h"
#import "GLESUtils.h"
@implementation OpenGLView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setupLayer];
[self setupContext];
[self setupProgram]; // load shader and run
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
+ (Class)layerClass {
// 只有 [CAEAGLLayer class] 类型的 layer 才支持在其上描绘 OpenGL 内容。
return [CAEAGLLayer class];
}
- (void)setupLayer
{
_eaglLayer = (CAEAGLLayer*) self.layer;
// CALayer 默认是透明的,必须将它设为不透明才能让其可见
_eaglLayer.opaque = YES;
// 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8
_eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}
- (void)setupContext {
// 指定 OpenGL 渲染 API 的版本,在这里我们使用 OpenGL ES 2.0
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context) {
NSLog(@"Failed to initialize OpenGLES 2.0 context");
exit(1);
}
// 设置为当前上下文
if (![EAGLContext setCurrentContext:_context]) {
_context = nil;
NSLog(@"Failed to set current OpenGL context");
exit(1);
}
}
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
// 设置为当前 renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// 为 color renderbuffer 分配存储空间
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
- (void)setupFrameBuffer {
glGenFramebuffers(1, &_frameBuffer);
// 设置为当前 framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
}
- (void)destoryRenderAndFrameBuffer
{
glDeleteFramebuffers(1, &_frameBuffer);
_frameBuffer = 0;
glDeleteRenderbuffers(1, &_colorRenderBuffer);
_colorRenderBuffer = 0;
}
- (void)render
{
glClearColor(0, 1.0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Setup viewport
//
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
GLfloat vertices[] = { // 三角形的點
0.0f, 0.5f, 0.0f, // 三角形起始點
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f // 三角形的結束點
};
// Load the vertex data
//
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
// Enable use of these arrays (they are disabled by default).
glEnableVertexAttribArray(_positionSlot);
// Draw triangle
//
glDrawArrays(GL_TRIANGLES, 0, 3);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)layoutSubviews {
[EAGLContext setCurrentContext:_context];
[self destoryRenderAndFrameBuffer];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self render];
}
//---------------------------------------------------
- (void)setupProgram
{
// Load shaders
//
NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
ofType:@"glsl"];
NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
ofType:@"glsl"];
// Create program, attach shaders, compile and link program
//
_programHandle = [GLESUtils loadProgram:vertexShaderPath
withFragmentShaderFilepath:fragmentShaderPath];
if (_programHandle == 0) {
NSLog(@" >> Error: Failed to setup program.");
return;
}
glUseProgram(_programHandle); // 執行 shader
// Get attribute slot from program
//
_positionSlot = glGetAttribLocation(_programHandle, "vPosition");
}
@end
7. 整個例子的檔案列表
8. 結果


沒有留言:
張貼留言