1. 先開一個專案
2.實作上並未用到storyboard,一切都使用動態模式,數學矩陣運算沿用前一個實作的程式,GLSL也是一樣,本例主要加上VBO的部分。檔案列表如下
3. 由於有手指旋轉的功能,因此在GLView.h/.mm上有需要加上手指接觸的指令,並且也需要加上VBO的接面。原本自動旋轉的功能則予以取消。
GLView.h
#import <UIKit/UIKit.h>
#import "IRenderingEngine.hpp"
#import <QuartzCore/QuartzCore.h>
@interface GLView : UIView {
@private
IRenderingEngine* m_renderingEngine;
EAGLContext* m_context;
float m_timestamp;
@public
BOOL ForceES1 ;
BOOL ForceVBO ;
}
- (void) drawView: (CADisplayLink*) displayLink;
- (id) initSet:(CGRect) frame;
@end
GLView.mm
#import "GLView.h"
@implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id) initWithFrame: (CGRect) frame
{
ForceES1 = NO;
ForceVBO = NO;
if (self = [super initWithFrame:frame])
{
if ([self initSet:frame] == nil)
return nil;
}
return self;
}
- (id) initSet:(CGRect) frame
{
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
eaglLayer.opaque = YES;
EAGLRenderingAPI api;
m_context = nil;
if (ForceES1 == NO){
api= kEAGLRenderingAPIOpenGLES2;
}
else {
api= kEAGLRenderingAPIOpenGLES1;
}
m_context = [[EAGLContext alloc] initWithAPI:api];
if (!m_context) {
api = kEAGLRenderingAPIOpenGLES1;
m_context = [[EAGLContext alloc] initWithAPI:api];
}
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
return nil;
}
if (api == kEAGLRenderingAPIOpenGLES1) {
NSLog(@"Using OpenGL ES 1.1");
if (ForceVBO == NO)
m_renderingEngine = CreateRenderer1();
else
m_renderingEngine = CreateVboRenderer1();
} else {
NSLog(@"Using OpenGL ES 2.0");
if (ForceVBO == NO)
m_renderingEngine = CreateRenderer2();
else
m_renderingEngine = CreateVboRenderer2();
}
[m_context
renderbufferStorage:GL_RENDERBUFFER
fromDrawable: eaglLayer];
m_renderingEngine->Initialize(CGRectGetWidth(frame), CGRectGetHeight(frame));
[self drawView: nil];
m_timestamp = CACurrentMediaTime();
CADisplayLink* displayLink;
displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(drawView:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
return self;
}
- (void) drawView: (CADisplayLink*) displayLink
{
if (displayLink != nil) {
float elapsedSeconds = displayLink.timestamp - m_timestamp;
m_timestamp = displayLink.timestamp;
m_renderingEngine->UpdateAnimation(elapsedSeconds);
}
m_renderingEngine->Render();
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
m_renderingEngine->OnFingerDown(ivec2(location.x, location.y));
}
- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
m_renderingEngine->OnFingerUp(ivec2(location.x, location.y));
}
- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint previous = [touch previousLocationInView: self];
CGPoint current = [touch locationInView: self];
m_renderingEngine->OnFingerMove(ivec2(previous.x, previous.y),
ivec2(current.x, current.y));
}
@end
4. IRenderingEngine.hpp要加上手指運作及VBO的部分
#include "Vector.hpp"
// Physical orientation of a handheld device; equivalent to UIDeviceOrientation
enum DeviceOrientation {
DeviceOrientationUnknown,
DeviceOrientationPortrait,
DeviceOrientationPortraitUpsideDown,
DeviceOrientationLandscapeLeft,
DeviceOrientationLandscapeRight,
DeviceOrientationFaceUp,
DeviceOrientationFaceDown,
};
// Creates an instance of the renderer and sets up various OpenGL state.
struct IRenderingEngine* CreateRenderer1();
struct IRenderingEngine* CreateRenderer2();
struct IRenderingEngine* CreateVboRenderer1();
struct IRenderingEngine* CreateVboRenderer2();
// Interface to the OpenGL ES renderer; consumed by Objective C.
struct IRenderingEngine {
virtual void Initialize(int width, int height) = 0;
virtual void Render() const = 0;
virtual void UpdateAnimation(float timeStep) = 0;
virtual void OnRotate(DeviceOrientation newOrientation) = 0;
virtual void OnFingerUp(ivec2 location) = 0;
virtual void OnFingerDown(ivec2 location) = 0;
virtual void OnFingerMove(ivec2 oldLocation, ivec2 newLocation) = 0;
virtual ~IRenderingEngine() {}
};
5.RenderingEngine1.cpp也是一樣的,還要加上接觸後放大,以及手指帶動旋轉的程式。
#include <OpenGLES/ES1/gl.h>#include <OpenGLES/ES1/glext.h>
#include "IRenderingEngine.hpp"
#include <vector>
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
class RenderingEngine1 : public IRenderingEngine {
public:
RenderingEngine1();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep) {}
void OnRotate(DeviceOrientation newOrientation) {}
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
private:
vector<Vertex> m_coneVertices;
vector<GLubyte> m_coneIndices;
GLfloat m_rotationAngle;
GLfloat m_scale;
ivec2 m_pivotPoint;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
int m_diskIndexCount;
int m_bodyIndexCount;
};
IRenderingEngine* CreateRenderer1()
{
return new RenderingEngine1();
}
RenderingEngine1::RenderingEngine1() : m_rotationAngle(0), m_scale(1)
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffersOES(1, &m_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
}
void RenderingEngine1::Initialize(int width, int height)
{
m_pivotPoint = ivec2(width / 2, height / 2);
const float coneRadius = 0.8f;
const float coneHeight = 1.866f;
const int coneSlices = 40;
const float dtheta = TwoPi / coneSlices;
const int vertexCount = coneSlices * 2 + 1;
const int diskCenterIndex = vertexCount - 1;
m_bodyIndexCount = coneSlices * 3;
m_diskIndexCount = coneSlices * 3;
m_coneVertices.resize(vertexCount);
vector<Vertex>::iterator vertex = m_coneVertices.begin();
// Cone's body
for (float theta = 0; vertex != m_coneVertices.end() - 1; theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness*2, brightness, brightness, 1); // 紅色
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
// Disk center
vertex->Position = vec3(0, 1 - coneHeight, 0);
vertex->Color = vec4(1, 1, 1, 1);
m_coneIndices.resize(m_bodyIndexCount + m_diskIndexCount);
vector<GLubyte>::iterator index = m_coneIndices.begin();
// Body triangles
for (int i = 0; i < coneSlices * 2; i += 2) {
*index++ = i;
*index++ = (i + 1) % (2 * coneSlices);
*index++ = (i + 3) % (2 * coneSlices);
}
// Disk triangles
for (int i = 1; i < coneSlices * 2 + 1; i += 2) {
*index++ = diskCenterIndex;
*index++ = i;
*index++ = (i + 2) % (2 * coneSlices);
}
// Create the depth buffer.
glGenRenderbuffersOES(1, &m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
GL_DEPTH_COMPONENT16_OES,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffersOES(1, &m_framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES,
m_colorRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_DEPTH_ATTACHMENT_OES,
GL_RENDERBUFFER_OES,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glFrustumf(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, 0, -7);
}
void RenderingEngine1::Render() const
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_coneVertices[0].Position.x;
const GLvoid* pColors = &m_coneVertices[0].Color.x;
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(m_rotationAngle, 0, 0, 1);
glScalef(m_scale, m_scale, m_scale);
glVertexPointer(3, GL_FLOAT, stride, pCoords);
glColorPointer(4, GL_FLOAT, stride, pColors);
glEnableClientState(GL_VERTEX_ARRAY);
const GLvoid* bodyIndices = &m_coneIndices[0];
const GLvoid* diskIndices = &m_coneIndices[m_bodyIndexCount];
glEnableClientState(GL_COLOR_ARRAY);
glDrawElements(GL_TRIANGLES, m_bodyIndexCount, GL_UNSIGNED_BYTE, bodyIndices);
glDisableClientState(GL_COLOR_ARRAY);
glColor4f(1, 1, 1, 1);
glDrawElements(GL_TRIANGLES, m_diskIndexCount, GL_UNSIGNED_BYTE, diskIndices);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}
void RenderingEngine1::OnFingerUp(ivec2 location)
{
m_scale = 1.0f;
}
void RenderingEngine1::OnFingerDown(ivec2 location)
{
m_scale = 1.5f;
OnFingerMove(location, location);
}
void RenderingEngine1::OnFingerMove(ivec2 previous, ivec2 location)
{
vec2 direction = vec2(location - m_pivotPoint).Normalized();
// Flip the Y axis because pixel coords increase towards the bottom.
direction.y = -direction.y;
m_rotationAngle = std::acos(direction.y) * 180.0f / 3.14159f;
if (direction.x > 0)
m_rotationAngle = -m_rotationAngle;
}
6.RenderingEngine2.cpp也是一樣的,同樣要加上接觸後放大,以及手指帶動旋轉的程式。
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "IRenderingEngine.hpp"
#include "Matrix.hpp"
#include <vector>
#include <iostream>
#define STRINGIFY(A) #A
#include "./Simple.vert"
#include "./Simple.frag"
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
class RenderingEngine2 : public IRenderingEngine {
public:
RenderingEngine2();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep) {}
void OnRotate(DeviceOrientation newOrientation) {}
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
private:
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
vector<Vertex> m_coneVertices;
vector<GLubyte> m_coneIndices;
GLfloat m_rotationAngle;
GLfloat m_scale;
ivec2 m_pivotPoint; // 樞紐
GLuint m_simpleProgram;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
int m_diskIndexCount;
int m_bodyIndexCount;
};
IRenderingEngine* CreateRenderer2()
{
return new RenderingEngine2();
}
RenderingEngine2::RenderingEngine2() : m_rotationAngle(0), m_scale(1)
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffers(1, &m_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
}
void RenderingEngine2::Initialize(int width, int height)
{
m_pivotPoint = ivec2(width / 2, height / 2);
const float coneRadius = 0.5f;
const float coneHeight = 1.866f;
const int coneSlices = 40;
const float dtheta = TwoPi / coneSlices;
const int vertexCount = coneSlices * 2 + 1;
const int diskCenterIndex = vertexCount - 1;
m_bodyIndexCount = coneSlices * 3;
m_diskIndexCount = coneSlices * 3;
m_coneVertices.resize(vertexCount);
vector<Vertex>::iterator vertex = m_coneVertices.begin();
// Cone's body
for (float theta = 0; vertex != m_coneVertices.end() - 1; theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness, brightness, 1); // 灰色,初始值
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
// Disk center
vertex->Position = vec3(0, 1 - coneHeight, 0);
vertex->Color = vec4(1, 1, 1, 1);
m_coneIndices.resize(m_bodyIndexCount + m_diskIndexCount);
vector<GLubyte>::iterator index = m_coneIndices.begin();
// Body triangles
for (int i = 0; i < coneSlices * 2; i += 2) {
*index++ = i;
*index++ = (i + 1) % (2 * coneSlices);
*index++ = (i + 3) % (2 * coneSlices);
}
// Disk triangles
for (int i = 1; i < coneSlices * 2 + 1; i += 2) {
*index++ = diskCenterIndex;
*index++ = i;
*index++ = (i + 2) % (2 * coneSlices);
}
// Create the depth buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
m_simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(m_simpleProgram);
// Set the projection matrix.
GLint projectionUniform = glGetUniformLocation(m_simpleProgram, "Projection");
mat4 projectionMatrix = mat4::Frustum(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glUniformMatrix4fv(projectionUniform, 1, 0, projectionMatrix.Pointer());
}
void RenderingEngine2::Render() const
{
GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");
GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");
mat4 rotation = mat4::Rotate(m_rotationAngle);
mat4 scale = mat4::Scale(m_scale);
mat4 translation = mat4::Translate(0, 0, -7);
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
mat4 modelviewMatrix = scale * rotation * translation;
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_coneVertices[0].Position.x;
const GLvoid* pColors = &m_coneVertices[0].Color.x;
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glEnableVertexAttribArray(positionSlot);
const GLvoid* bodyIndices = &m_coneIndices[0];
const GLvoid* diskIndices = &m_coneIndices[m_bodyIndexCount];
glEnableVertexAttribArray(colorSlot);
glDrawElements(GL_TRIANGLES, m_bodyIndexCount, GL_UNSIGNED_BYTE, bodyIndices);
glDisableVertexAttribArray(colorSlot);
glVertexAttrib4f(colorSlot, 1, 1, 1, 1);
glDrawElements(GL_TRIANGLES, m_diskIndexCount, GL_UNSIGNED_BYTE, diskIndices);
glDisableVertexAttribArray(positionSlot);
}
void RenderingEngine2::OnFingerUp(ivec2 location)
{
m_scale = 1.0f;
}
void RenderingEngine2::OnFingerDown(ivec2 location)
{
m_scale = 1.5f;
OnFingerMove(location, location);
}
void RenderingEngine2::OnFingerMove(ivec2 previous, ivec2 location)
{
vec2 direction = vec2(location - m_pivotPoint).Normalized();
// Flip the Y axis because pixel coords increase towards the bottom.
direction.y = -direction.y;
m_rotationAngle = std::acos(direction.y) * 180.0f / 3.14159f;
if (direction.x > 0)
m_rotationAngle = -m_rotationAngle;
}
GLuint RenderingEngine2::BuildShader(const char* source, GLenum shaderType) const
{
GLuint shaderHandle = glCreateShader(shaderType);
glShaderSource(shaderHandle, 1, &source, 0);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
std::cout << messages;
exit(1);
}
return shaderHandle;
}
GLuint RenderingEngine2::BuildProgram(const char* vertexShaderSource,
const char* fragmentShaderSource) const
{
GLuint vertexShader = BuildShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint fragmentShader = BuildShader(fragmentShaderSource, 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]);
std::cout << messages;
exit(1);
}
return programHandle;
}
7.VboRenderingEngine1.cpp除了是VBO的功能之外,同樣也有接觸後放大,以及手指帶動旋轉的程式。
#include <OpenGLES/ES1/gl.h>#include <OpenGLES/ES1/glext.h>
#include "IRenderingEngine.hpp"
#include <vector>
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
class VboRenderingEngine1 : public IRenderingEngine {
public:
VboRenderingEngine1();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep) {}
void OnRotate(DeviceOrientation newOrientation) {}
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
private:
GLuint m_vertexBuffer;
GLuint m_indexBuffer;
GLfloat m_rotationAngle;
GLfloat m_scale;
ivec2 m_pivotPoint;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
int m_diskIndexCount;
int m_bodyIndexCount;
};
IRenderingEngine* CreateVboRenderer1()
{
return new VboRenderingEngine1();
}
VboRenderingEngine1::VboRenderingEngine1() : m_rotationAngle(0), m_scale(1)
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffersOES(1, &m_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
}
void VboRenderingEngine1::Initialize(int width, int height)
{
m_pivotPoint = ivec2(width / 2, height / 2);
const float coneRadius = 0.8f;
const float coneHeight = 1.866f;
const int coneSlices = 40;
const float dtheta = TwoPi / coneSlices;
const int vertexCount = coneSlices * 2 + 1;
const int diskCenterIndex = vertexCount - 1;
m_bodyIndexCount = coneSlices * 3;
m_diskIndexCount = coneSlices * 3;
vector<Vertex> coneVertices(vertexCount);
vector<Vertex>::iterator vertex = coneVertices.begin();
// Cone's body
for (float theta = 0; vertex != coneVertices.end() - 1; theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness, brightness*2, 1); // 藍色
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
// Disk center
vertex->Position = vec3(0, 1 - coneHeight, 0);
vertex->Color = vec4(1, 1, 1, 1);
// Create the VBO for the vertices.
glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
coneVertices.size() * sizeof(coneVertices[0]),
&coneVertices[0],
GL_STATIC_DRAW);
vector<GLubyte> coneIndices(m_bodyIndexCount + m_diskIndexCount);
vector<GLubyte>::iterator index = coneIndices.begin();
// Body triangles
for (int i = 0; i < coneSlices * 2; i += 2) {
*index++ = i;
*index++ = (i + 1) % (2 * coneSlices);
*index++ = (i + 3) % (2 * coneSlices);
}
// Disk triangles
for (int i = 1; i < coneSlices * 2 + 1; i += 2) {
*index++ = diskCenterIndex;
*index++ = i;
*index++ = (i + 2) % (2 * coneSlices);
}
// Create the VBO for the indices.
glGenBuffers(1, &m_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
coneIndices.size() * sizeof(coneIndices[0]),
&coneIndices[0],
GL_STATIC_DRAW);
// Create the depth buffer.
glGenRenderbuffersOES(1, &m_depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
GL_DEPTH_COMPONENT16_OES,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffersOES(1, &m_framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES,
m_colorRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_DEPTH_ATTACHMENT_OES,
GL_RENDERBUFFER_OES,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glFrustumf(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, 0, -7);
}
void VboRenderingEngine1::Render() const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(m_rotationAngle, 0, 0, 1);
glScalef(m_scale, m_scale, m_scale);
const GLvoid* colorOffset = (GLvoid*) sizeof(vec3);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
glColorPointer(4, GL_FLOAT, sizeof(Vertex), colorOffset);
glEnableClientState(GL_VERTEX_ARRAY);
const GLvoid* bodyOffset = 0;
const GLvoid* diskOffset = (GLvoid*) m_bodyIndexCount;
glEnableClientState(GL_COLOR_ARRAY);
glDrawElements(GL_TRIANGLES, m_bodyIndexCount, GL_UNSIGNED_BYTE, bodyOffset);
glDisableClientState(GL_COLOR_ARRAY);
glColor4f(1, 1, 1, 1);
glDrawElements(GL_TRIANGLES, m_diskIndexCount, GL_UNSIGNED_BYTE, diskOffset);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}
void VboRenderingEngine1::OnFingerUp(ivec2 location)
{
m_scale = 1.0f;
}
void VboRenderingEngine1::OnFingerDown(ivec2 location)
{
m_scale = 1.5f;
OnFingerMove(location, location);
}
void VboRenderingEngine1::OnFingerMove(ivec2 previous, ivec2 location)
{
vec2 direction = vec2(location - m_pivotPoint).Normalized();
// Flip the Y axis because pixel coords increase towards the bottom.
direction.y = -direction.y;
m_rotationAngle = std::acos(direction.y) * 180.0f / 3.14159f;
if (direction.x > 0)
m_rotationAngle = -m_rotationAngle;
}
8.VboRenderingEngine2.cpp除了是VBO的功能之外,同樣也有接觸後放大,以及手指帶動旋轉的程式。
#include <OpenGLES/ES2/gl.h>#include <OpenGLES/ES2/glext.h>
#include "IRenderingEngine.hpp"
#include "Matrix.hpp"
#include <vector>
#include <iostream>
#define STRINGIFY(A) #A
#include "./Simple.vert"
#include "./Simple.frag"
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
class VboRenderingEngine2 : public IRenderingEngine {
public:
VboRenderingEngine2();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep) {}
void OnRotate(DeviceOrientation newOrientation) {}
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
private:
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
GLuint m_vertexBuffer; // Object handles for VBO
GLuint m_indexBuffer; // Object handles for VBO
GLfloat m_rotationAngle;
GLfloat m_scale;
ivec2 m_pivotPoint;
GLuint m_simpleProgram;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
int m_diskIndexCount;
int m_bodyIndexCount;
};
IRenderingEngine* CreateVboRenderer2()
{
return new VboRenderingEngine2();
}
VboRenderingEngine2::VboRenderingEngine2() : m_rotationAngle(0), m_scale(1)
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffers(1, &m_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
}
void VboRenderingEngine2::Initialize(int width, int height)
{
m_pivotPoint = ivec2(width / 2, height / 2);
const float coneRadius = 0.5f;
const float coneHeight = 1.866f;
const int coneSlices = 40;
const float dtheta = TwoPi / coneSlices;
const int vertexCount = coneSlices * 2 + 1;
const int diskCenterIndex = vertexCount - 1;
m_bodyIndexCount = coneSlices * 3;
m_diskIndexCount = coneSlices * 3;
vector<Vertex> coneVertices(vertexCount);
vector<Vertex>::iterator vertex = coneVertices.begin();
// Cone's body
for (float theta = 0; vertex != coneVertices.end() - 1; theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness*2, brightness, brightness*2, 1); // 紫色
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
// Disk center
vertex->Position = vec3(0, 1 - coneHeight, 0);
vertex->Color = vec4(1, 1, 1, 1);
// Create the VBO for the vertices.
glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
coneVertices.size() * sizeof(coneVertices[0]),
&coneVertices[0],
GL_STATIC_DRAW);
vector<GLubyte> coneIndices(m_bodyIndexCount + m_diskIndexCount);
vector<GLubyte>::iterator index = coneIndices.begin();
// Body triangles
for (int i = 0; i < coneSlices * 2; i += 2) {
*index++ = i;
*index++ = (i + 1) % (2 * coneSlices);
*index++ = (i + 3) % (2 * coneSlices);
}
// Disk triangles
for (int i = 1; i < coneSlices * 2 + 1; i += 2) {
*index++ = diskCenterIndex;
*index++ = i;
*index++ = (i + 2) % (2 * coneSlices);
}
// Create the VBO for the indices.
glGenBuffers(1, &m_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
coneIndices.size() * sizeof(coneIndices[0]),
&coneIndices[0],
GL_STATIC_DRAW);
// Create the depth buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
m_simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(m_simpleProgram);
// Set the projection matrix.
GLint projectionUniform = glGetUniformLocation(m_simpleProgram, "Projection");
mat4 projectionMatrix = mat4::Frustum(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glUniformMatrix4fv(projectionUniform, 1, 0, projectionMatrix.Pointer());
}
void VboRenderingEngine2::Render() const
{
GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");
GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");
mat4 rotation = mat4::Rotate(m_rotationAngle);
mat4 scale = mat4::Scale(m_scale);
mat4 translation = mat4::Translate(0, 0, -7);
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
mat4 modelviewMatrix = scale * rotation * translation;
GLsizei stride = sizeof(Vertex);
const GLvoid* colorOffset = (GLvoid*) sizeof(vec3);
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, 0);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, colorOffset);
glEnableVertexAttribArray(positionSlot);
const GLvoid* bodyOffset = 0;
const GLvoid* diskOffset = (GLvoid*) m_bodyIndexCount;
glEnableVertexAttribArray(colorSlot);
glDrawElements(GL_TRIANGLES, m_bodyIndexCount, GL_UNSIGNED_BYTE, bodyOffset);
glDisableVertexAttribArray(colorSlot);
glVertexAttrib4f(colorSlot, 1, 1, 1, 1);
glDrawElements(GL_TRIANGLES, m_diskIndexCount, GL_UNSIGNED_BYTE, diskOffset);
glDisableVertexAttribArray(positionSlot);
}
void VboRenderingEngine2::OnFingerUp(ivec2 location)
{
m_scale = 1.0f;
}
void VboRenderingEngine2::OnFingerDown(ivec2 location)
{
m_scale = 1.5f;
OnFingerMove(location, location);
}
void VboRenderingEngine2::OnFingerMove(ivec2 previous, ivec2 location)
{
vec2 direction = vec2(location - m_pivotPoint).Normalized();
// Flip the Y axis because pixel coords increase towards the bottom.
direction.y = -direction.y;
m_rotationAngle = std::acos(direction.y) * 180.0f / 3.14159f;
if (direction.x > 0)
m_rotationAngle = -m_rotationAngle;
}
GLuint VboRenderingEngine2::BuildShader(const char* source, GLenum shaderType) const
{
GLuint shaderHandle = glCreateShader(shaderType);
glShaderSource(shaderHandle, 1, &source, 0);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
std::cout << messages;
exit(1);
}
return shaderHandle;
}
GLuint VboRenderingEngine2::BuildProgram(const char* vertexShaderSource,
const char* fragmentShaderSource) const
{
GLuint vertexShader = BuildShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint fragmentShader = BuildShader(fragmentShaderSource, 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]);
std::cout << messages;
exit(1);
}
return programHandle;
}
9.mainViewController.h主控區定義與前一個實作相同。
#import <UIKit/UIKit.h>
#include "GLView.h"
@interface mainViewController : UIViewController
{
UIWindow* m_window;
GLView* controllView;
}
@end
10.mainViewController.mm主控區程式與前一個實作大致相同,只是多了一個VBO切換的Button。
#import "mainViewController.h"@interface mainViewController ()
@end
@implementation mainViewController
{
UIButton *swBtn;
UIButton *swBtn2;
}
BOOL mode1;
BOOL isVBO;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect screenBounds = [[UIScreen mainScreen] bounds];
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
controllView = [[GLView alloc] initWithFrame: screenBounds];
[self setButtonInterface];
[self setButtonInterface2];
[controllView addSubview:swBtn];
[controllView addSubview:swBtn2];
[m_window addSubview: controllView];
[m_window makeKeyAndVisible];
mode1 = NO;
isVBO = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)setButtonInterface // 動態產生一個的Button
{
swBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//動態產生一個RoundedRect 形式的 Button
swBtn.frame = CGRectMake(0,0, 100, 30); // 大小
[swBtn setCenter:CGPointMake(150, 50)];//位置放在x=150, y=50的位置
[swBtn addTarget:self action:@selector(onSwitch) forControlEvents:UIControlEventTouchUpInside];
//設定Button動作呼叫的function在 onHelloActionButton,方式為按下
//_helloActionButton.= @"Action Button";
[swBtn setTitle:@"ES2 Mode" forState:UIControlStateNormal];
//將動態Button上放置Action Button這兩個字
//[self.view addSubview:swBtn];
//將動態Button放到View上展出
}
- (void)setButtonInterface2 // 動態產生一個的Button
{
swBtn2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
//動態產生一個RoundedRect 形式的 Button
swBtn2.frame = CGRectMake(0,0, 100, 30); // 大小
[swBtn2 setCenter:CGPointMake(350, 50)];//位置放在x=150, y=50的位置
[swBtn2 addTarget:self action:@selector(onSwitch1) forControlEvents:UIControlEventTouchUpInside];
//設定Button動作呼叫的function在 onHelloActionButton,方式為按下
//_helloActionButton.= @"Action Button";
[swBtn2 setTitle:@"Normal Mode" forState:UIControlStateNormal];
//將動態Button上放置Action Button這兩個字
//[self.view addSubview:swBtn2];
//將動態Button放到View上展出
}
- (void) onSwitch
{
mode1 = ~mode1;
controllView->ForceES1 = mode1;
[controllView initSet:m_window.frame];
if (mode1 == 0){
[swBtn setTitle:@"ES2 Mode" forState:UIControlStateNormal];
}
else
[swBtn setTitle:@"ES1 Mode" forState:UIControlStateNormal];
}
- (void) onSwitch1
{
isVBO = ~isVBO;
controllView->ForceVBO = isVBO;
[controllView initSet:m_window.frame];
if (isVBO == 0){
[swBtn2 setTitle:@"Normal Mode" forState:UIControlStateNormal];
}
else
[swBtn2 setTitle:@"VBO Mode" forState:UIControlStateNormal];
}
@end






沒有留言:
張貼留言