版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.01.08 |
前言
OpenGL ES图形库项目中一直也没用过,最近也想学着使用这个图形库,感觉还是很有意思,也就自然想着好好的总结一下,希望对大家能有所帮助。下面就开始进行实践,写一些小程序。感兴趣的可以看上面几篇文章。
1. OpenGL ES实践(一)—— 一个简单的小程序
2. OpenGL ES实践(二)—— 一个简单的三角形
3. OpenGL ES实践(三)—— 一个三色三角形的绘制
4. OpenGL ES实践(四)—— 简单图形的旋转
功能要求
使用一个纹理来给几何图形对象着色。
功能实现
先看下结构
下面看下代码
1. JJOpenglesVC.h
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
@class JJGLKElementIndexArrayBuffer;
@interface JJOpenglesVC : GLKViewController
@property (strong, nonatomic) GLKBaseEffect *baseEffect;
@property (strong, nonatomic) JJGLKElementIndexArrayBuffer *vertexBuffer;
@end
2. JJOpenglesVC.m
#import "JJOpenglesVC.h"
#import "JJGLKElementIndexArrayBuffer.h"
#import "JJGLKContext.h"
/////////////////////////////////////////////////////////////////
// This data type is used to store information for each vertex
typedef struct {
GLKVector3 positionCoords;
GLKVector2 textureCoords;
}
SceneVertex;
/////////////////////////////////////////////////////////////////
// Define vertex data for a triangle to use in example
static const SceneVertex vertices[] =
{
{{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f}}, // lower left corner
{{ 0.5f, -0.5f, 0.0f}, {1.0f, 0.0f}}, // lower right corner
{{-0.5f, 0.5f, 0.0f}, {0.0f, 1.0f}}, // upper left corner
};
@interface JJOpenglesVC()
@end
@implementation JJOpenglesVC
@synthesize baseEffect;
@synthesize vertexBuffer;
#pragma mark - Override Base Function
/////////////////////////////////////////////////////////////////
// Called when the view controller's view is loaded
// Perform initialization before the view is asked to draw
- (void)viewDidLoad
{
[super viewDidLoad];
// Verify the type of view created automatically by the
// Interface Builder storyboard
GLKView *view = (GLKView *)self.view;
NSAssert([view isKindOfClass:[GLKView class]],
@"View controller's view is not a GLKView");
// Create an OpenGL ES 2.0 context and provide it to the
// view
view.context = [[JJGLKContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES3];
// Make the new context current
[JJGLKContext setCurrentContext:view.context];
// Create a base effect that provides standard OpenGL ES 2.0
// shading language programs and set constants to be used for
// all subsequent rendering
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.baseEffect.constantColor = GLKVector4Make(
1.0f, // Red
1.0f, // Green
1.0f, // Blue
1.0f);// Alpha
// Set the background color stored in the current context
((JJGLKContext *)view.context).clearColor = GLKVector4Make(
0.6f, // Red
0.5f, // Green
0.4f, // Blue
1.0f);// Alpha
// Create vertex buffer containing vertices to draw
self.vertexBuffer = [[JJGLKElementIndexArrayBuffer alloc]
initWithAttribStride:sizeof(SceneVertex)
numberOfVertices:sizeof(vertices) / sizeof(SceneVertex)
bytes:vertices
usage:GL_STATIC_DRAW];
// Setup texture
CGImageRef imageRef = [[UIImage imageNamed:@"mountain"] CGImage];
GLKTextureInfo *textureInfo = [GLKTextureLoader
textureWithCGImage:imageRef
options:nil
error:NULL];
self.baseEffect.texture2d0.name = textureInfo.name;
self.baseEffect.texture2d0.target = textureInfo.target;
}
/////////////////////////////////////////////////////////////////
// Called when the view controller's view has been unloaded
// Perform clean-up that is possible when you know the view
// controller's view won't be asked to draw again soon.
- (void)viewDidUnload
{
[super viewDidUnload];
// Make the view's context current
GLKView *view = (GLKView *)self.view;
[JJGLKContext setCurrentContext:view.context];
// Delete buffers that aren't needed when view is unloaded
self.vertexBuffer = nil;
// Stop using the context created in -viewDidLoad
((GLKView *)self.view).context = nil;
[EAGLContext setCurrentContext:nil];
}
#pragma mark - GLKViewDelegate
/////////////////////////////////////////////////////////////////
// GLKView delegate method: Called by the view controller's view
// whenever Cocoa Touch asks the view controller's view to
// draw itself. (In this case, render into a frame buffer that
// shares memory with a Core Animation Layer)
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
[self.baseEffect prepareToDraw];
// Clear back frame buffer (erase previous drawing)
[(JJGLKContext *)view.context clear:GL_COLOR_BUFFER_BIT];
[self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition
numberOfCoordinates:3
attribOffset:offsetof(SceneVertex, positionCoords)
shouldEnable:YES];
[self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribTexCoord0
numberOfCoordinates:2
attribOffset:offsetof(SceneVertex, textureCoords)
shouldEnable:YES];
// Draw triangles using the first three vertices in the
// currently bound vertex buffer
[self.vertexBuffer drawArrayWithMode:GL_TRIANGLES
startVertexIndex:0
numberOfVertices:3];
}
@end
3. JJGLKContext.h
#import <GLKit/GLKit.h>
@interface JJGLKContext : EAGLContext
{
GLKVector4 clearColor;
}
@property (nonatomic, assign) GLKVector4 clearColor;
- (void)clear:(GLbitfield)mask;
- (void)enable:(GLenum)capability;
- (void)disable:(GLenum)capability;
- (void)setBlendSourceFunction:(GLenum)sfactor
destinationFunction:(GLenum)dfactor;
@end
4. JJGLKContext.m
#import "JJGLKContext.h"
@implementation JJGLKContext
/////////////////////////////////////////////////////////////////
// This method sets the clear (background) RGBA color.
// The clear color is undefined until this method is called.
- (void)setClearColor:(GLKVector4)clearColorRGBA
{
clearColor = clearColorRGBA;
NSAssert(self == [[self class] currentContext],
@"Receiving context required to be current context");
glClearColor(
clearColorRGBA.r,
clearColorRGBA.g,
clearColorRGBA.b,
clearColorRGBA.a);
}
/////////////////////////////////////////////////////////////////
// Returns the clear (background) color set via -setClearColor:.
// If no clear color has been set via -setClearColor:, the
// return clear color is undefined.
- (GLKVector4)clearColor
{
return clearColor;
}
/////////////////////////////////////////////////////////////////
// This method instructs OpenGL ES to set all data in the
// current Context's Render Buffer(s) identified by mask to
// colors (values) specified via -setClearColor: and/or
// OpenGL ES functions for each Render Buffer type.
- (void)clear:(GLbitfield)mask
{
NSAssert(self == [[self class] currentContext],
@"Receiving context required to be current context");
glClear(mask);
}
/////////////////////////////////////////////////////////////////
- (void)enable:(GLenum)capability;
{
NSAssert(self == [[self class] currentContext],
@"Receiving context required to be current context");
glEnable(capability);
}
/////////////////////////////////////////////////////////////////
//
- (void)disable:(GLenum)capability;
{
NSAssert(self == [[self class] currentContext],
@"Receiving context required to be current context");
glDisable(capability);
}
/////////////////////////////////////////////////////////////////
//
- (void)setBlendSourceFunction:(GLenum)sfactor
destinationFunction:(GLenum)dfactor;
{
glBlendFunc(sfactor, dfactor);
}
@end
5. JJGLKElementIndexArrayBuffer.h
#import <GLKit/GLKit.h>
@class JJGLKElementIndexArrayBuffer;
/////////////////////////////////////////////////////////////////
//
typedef enum {
AGLKVertexAttribPosition = GLKVertexAttribPosition,
AGLKVertexAttribNormal = GLKVertexAttribNormal,
AGLKVertexAttribColor = GLKVertexAttribColor,
AGLKVertexAttribTexCoord0 = GLKVertexAttribTexCoord0,
AGLKVertexAttribTexCoord1 = GLKVertexAttribTexCoord1,
} AGLKVertexAttrib;
@interface JJGLKElementIndexArrayBuffer : NSObject
{
GLsizei stride;
GLsizeiptr bufferSizeBytes;
GLuint name;
}
@property (nonatomic, readonly) GLuint name;
@property (nonatomic, readonly) GLsizeiptr bufferSizeBytes;
@property (nonatomic, readonly) GLsizei stride;
+ (void)drawPreparedArraysWithMode:(GLenum)mode
startVertexIndex:(GLint)first
numberOfVertices:(GLsizei)count;
- (id)initWithAttribStride:(GLsizei)stride
numberOfVertices:(GLsizei)count
bytes:(const GLvoid *)dataPtr
usage:(GLenum)usage;
- (void)prepareToDrawWithAttrib:(GLuint)index
numberOfCoordinates:(GLint)count
attribOffset:(GLsizeiptr)offset
shouldEnable:(BOOL)shouldEnable;
- (void)drawArrayWithMode:(GLenum)mode
startVertexIndex:(GLint)first
numberOfVertices:(GLsizei)count;
- (void)reinitWithAttribStride:(GLsizei)stride
numberOfVertices:(GLsizei)count
bytes:(const GLvoid *)dataPtr;
@end
6. JJGLKElementIndexArrayBuffer.m
#import "JJGLKElementIndexArrayBuffer.h"
@interface JJGLKElementIndexArrayBuffer ()
@property (nonatomic, assign) GLsizeiptr bufferSizeBytes;
@property (nonatomic, assign) GLsizei stride;
@end
@implementation JJGLKElementIndexArrayBuffer
@synthesize name;
@synthesize bufferSizeBytes;
@synthesize stride;
/////////////////////////////////////////////////////////////////
// This method creates a vertex attribute array buffer in
// the current OpenGL ES context for the thread upon which this
// method is called.
- (id)initWithAttribStride:(GLsizei)aStride
numberOfVertices:(GLsizei)count
bytes:(const GLvoid *)dataPtr
usage:(GLenum)usage;
{
NSParameterAssert(0 < aStride);
NSAssert((0 < count && NULL != dataPtr) ||
(0 == count && NULL == dataPtr),
@"data must not be NULL or count > 0");
if(nil != (self = [super init]))
{
stride = aStride;
bufferSizeBytes = stride * count;
glGenBuffers(1, // STEP 1
&name);
glBindBuffer(GL_ARRAY_BUFFER, // STEP 2
self.name);
glBufferData( // STEP 3
GL_ARRAY_BUFFER, // Initialize buffer contents
bufferSizeBytes, // Number of bytes to copy
dataPtr, // Address of bytes to copy
usage); // Hint: cache in GPU memory
NSAssert(0 != name, @"Failed to generate name");
}
return self;
}
/////////////////////////////////////////////////////////////////
// This method loads the data stored by the receiver.
- (void)reinitWithAttribStride:(GLsizei)aStride
numberOfVertices:(GLsizei)count
bytes:(const GLvoid *)dataPtr;
{
NSParameterAssert(0 < aStride);
NSParameterAssert(0 < count);
NSParameterAssert(NULL != dataPtr);
NSAssert(0 != name, @"Invalid name");
self.stride = aStride;
self.bufferSizeBytes = aStride * count;
glBindBuffer(GL_ARRAY_BUFFER, // STEP 2
self.name);
glBufferData( // STEP 3
GL_ARRAY_BUFFER, // Initialize buffer contents
bufferSizeBytes, // Number of bytes to copy
dataPtr, // Address of bytes to copy
GL_DYNAMIC_DRAW);
}
/////////////////////////////////////////////////////////////////
// A vertex attribute array buffer must be prepared when your
// application wants to use the buffer to render any geometry.
// When your application prepares an buffer, some OpenGL ES state
// is altered to allow bind the buffer and configure pointers.
- (void)prepareToDrawWithAttrib:(GLuint)index
numberOfCoordinates:(GLint)count
attribOffset:(GLsizeiptr)offset
shouldEnable:(BOOL)shouldEnable
{
NSParameterAssert((0 < count) && (count < 4));
NSParameterAssert(offset < self.stride);
NSAssert(0 != name, @"Invalid name");
glBindBuffer(GL_ARRAY_BUFFER, // STEP 2
self.name);
if(shouldEnable)
{
glEnableVertexAttribArray( // Step 4
index);
}
glVertexAttribPointer( // Step 5
index, // Identifies the attribute to use
count, // number of coordinates for attribute
GL_FLOAT, // data is floating point
GL_FALSE, // no fixed point scaling
(self.stride), // total num bytes stored per vertex
NULL + offset); // offset from start of each vertex to
// first coord for attribute
#ifdef DEBUG
{ // Report any errors
GLenum error = glGetError();
if(GL_NO_ERROR != error)
{
NSLog(@"GL Error: 0x%x", error);
}
}
#endif
}
/////////////////////////////////////////////////////////////////
// Submits the drawing command identified by mode and instructs
// OpenGL ES to use count vertices from the buffer starting from
// the vertex at index first. Vertex indices start at 0.
- (void)drawArrayWithMode:(GLenum)mode
startVertexIndex:(GLint)first
numberOfVertices:(GLsizei)count
{
NSAssert(self.bufferSizeBytes >=
((first + count) * self.stride),
@"Attempt to draw more vertex data than available.");
glDrawArrays(mode, first, count); // Step 6
}
/////////////////////////////////////////////////////////////////
// Submits the drawing command identified by mode and instructs
// OpenGL ES to use count vertices from previously prepared
// buffers starting from the vertex at index first in the
// prepared buffers
+ (void)drawPreparedArraysWithMode:(GLenum)mode
startVertexIndex:(GLint)first
numberOfVertices:(GLsizei)count;
{
glDrawArrays(mode, first, count); // Step 6
}
/////////////////////////////////////////////////////////////////
// This method deletes the receiver's buffer from the current
// Context when the receiver is deallocated.
- (void)dealloc
{
// Delete buffer from current context
if (0 != name)
{
glDeleteBuffers (1, &name); // Step 7
name = 0;
}
}
@end
功能效果
下面看一下功能效果。
后记
未完,待续~~~