1. 先開一個專案
2. 加入所需 framework以及所新增的檔名,如下圖
3. storyboard下增加一個Button,用來切換ES1及ES2兩種模式的顯示方式。
4.. 先新增GLSL的檔案,每個檔案的第一行是使用Define的方式,來作為c string的取代,此處與前幾個實作方式不同,前面數例使用objectC的檔案讀入。
frag.glsl
const char* SimpleFragmentShader = STRINGIFY(
varying lowp vec4 DestinationColor;
void main(void)
{
gl_FragColor = DestinationColor;
}
);
vertex.glsl
const char* SimpleVertexShader = STRINGIFY(
attribute vec4 Position;
attribute vec4 SourceColor;
varying vec4 DestinationColor;
uniform mat4 Projection;
uniform mat4 Modelview;
void main(void)
{
DestinationColor = SourceColor;
gl_Position = Projection * Modelview * Position;
}
);
5. 加入IRenderingEngine.hpp的內容
// 此部分改由c++寫成,為因應RenderingEngine1/2.cpp下方向的計算,而不用原本Xcode下所帶的方向定義,但兩者之間的定義值是一樣的,這是因為objectC不能為C++呼叫及聯結,反之可以,
enum DeviceOrientation2 {
DeviceOrientationUnknown,
DeviceOrientationPortrait,
DeviceOrientationPortraitUpsideDown,
DeviceOrientationLandscapeLeft,
DeviceOrientationLandscapeRight,
DeviceOrientationFaceUp,
DeviceOrientationFaceDown,
};
// Create an instance of the renderer and set up various OpenGL state.
struct IRenderingEngine* CreateRenderer1();
struct IRenderingEngine* CreateRenderer2();
// Interface to the OpenGL ES renderer; consumed by GLView.
struct IRenderingEngine { // 對應
virtual void Initialize(int width, int height) = 0;
virtual void Render() const = 0;
virtual void UpdateAnimation(float timeStep) = 0;
virtual void OnRotate(DeviceOrientation2 newOrientation) = 0;
virtual ~IRenderingEngine() {}
};
6. RenderingEngine1.cpp
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include "IRenderingEngine.hpp"
static const float RevolutionsPerSecond = 1;
class RenderingEngine1 : public IRenderingEngine {
public:
RenderingEngine1();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep);
void OnRotate(DeviceOrientation2 newOrientation); // DeviceOrientation
private:
float RotationDirection() const;
float m_desiredAngle;
float m_currentAngle;
GLuint m_framebuffer;
GLuint m_renderbuffer;
};
IRenderingEngine* CreateRenderer1()
{
return new RenderingEngine1();
}
struct Vertex {
float Position[2];
float Color[4];
};
// Define the positions and colors of two triangles.
const Vertex Vertices[] = {
{{-0.5, -0.866}, {1, 1, 1.0f, 1}},
{{0.5, -0.866}, {1, 1, 1.0f, 1}},
{{0, 1}, {1, 1, 1.0f, 1}},
{{-0.5, -0.866}, {0.5f, 1.5f, 0.5f}},
{{0.5, -0.866}, {0.5f, 1.5f, 0.5f}},
{{0, -0.4f}, {0.5f, 1.5f, 0.5f}},
};
RenderingEngine1::RenderingEngine1()
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffersOES(1, &m_renderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);
}
void RenderingEngine1::Initialize(int width, int height)
{
// Create the framebuffer object and attach the color buffer.
glGenFramebuffersOES(1, &m_framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES,
GL_RENDERBUFFER_OES,
m_renderbuffer);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
// Initialize the projection matrix.
const float maxX = 2;
const float maxY = 3;
glOrthof(-maxX, +maxX, -maxY, +maxY, -1, 1);
glMatrixMode(GL_MODELVIEW);
// Initialize the rotation animation state.
OnRotate(DeviceOrientationPortrait);
m_currentAngle = m_desiredAngle;
}
void RenderingEngine1::Render() const
{
glClearColor(0.5f, 0.5f, 1.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glRotatef(m_currentAngle, 0, 0, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &Vertices[0].Position[0]);
glColorPointer(4, GL_FLOAT, sizeof(Vertex), &Vertices[0].Color[0]);
GLsizei vertexCount = sizeof(Vertices) / sizeof(Vertex);
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopMatrix();
}
float RenderingEngine1::RotationDirection() const
{
float delta = m_desiredAngle - m_currentAngle;
if (delta == 0)
return 0;
bool counterclockwise = ((delta > 0 && delta <= 180) || (delta < -180));
return counterclockwise ? +1 : -1;
}
void RenderingEngine1::UpdateAnimation(float timeStep)
{
float direction = RotationDirection();
if (direction == 0)
return;
float degrees = timeStep * 360 * RevolutionsPerSecond;
m_currentAngle += degrees * direction;
// Normalize the angle to [0, 360)
if (m_currentAngle >= 360)
m_currentAngle -= 360;
else if (m_currentAngle < 0)
m_currentAngle += 360;
// If the rotation direction changed, then we overshot the desired angle.
if (RotationDirection() != direction)
m_currentAngle = m_desiredAngle;
}
void RenderingEngine1::OnRotate(DeviceOrientation2 orientation) // DeviceOrientation
{
float angle = 0;
switch (orientation) {
case DeviceOrientationLandscapeLeft:
angle = 90; //
break;
case DeviceOrientationPortraitUpsideDown:
angle = 180;
break;
case DeviceOrientationLandscapeRight:
angle = 270;
break;
default:
angle = 0;
break;
}
m_desiredAngle = angle;
}
7. RenderingEngine2.cpp
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include <cmath>
#include <iostream>
#include "IRenderingEngine.hpp"
#define STRINGIFY(A) #A
#include "./frag.glsl"
#include "./vertex.glsl"
static const float RevolutionsPerSecond = 0.1;
class RenderingEngine2 : public IRenderingEngine {
public:
RenderingEngine2();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep);
void OnRotate(DeviceOrientation2 newOrientation); // DeviceOrientation
private:
float RotationDirection() const;
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
void ApplyOrtho(float maxX, float maxY) const;
void ApplyRotation(float degrees) const;
double m_desiredAngle;
double m_currentAngle;
GLuint m_simpleProgram;
GLuint m_framebuffer;
GLuint m_renderbuffer;
};
IRenderingEngine* CreateRenderer2()
{
return new RenderingEngine2();
}
struct Vertex {
float Position[2];
float Color[4];
};
// Define the positions and colors of two triangles.
const Vertex Vertices[] = {
{{-0.5, -0.866}, {1, 1, 0.5f, 1}}, // (x,y) + (r,g,b,a)
{{0.5, -0.866}, {1, 1, 0.5f, 1}},
{{0, 1}, {1, 1, 0.5f, 1}},
{{-0.5, -0.866}, {0.5f, 0.5f, 0.5f}},
{{0.5, -0.866}, {0.5f, 0.5f, 0.5f}},
{{0, -0.4f}, {0.5f, 0.5f, 0.5f}},
};
RenderingEngine2::RenderingEngine2()
{
// Create & bind the color buffer so that the caller can allocate its space.
glGenRenderbuffers(1, &m_renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
}
void RenderingEngine2::Initialize(int width, int height)
{
// Create the framebuffer object and attach the color buffer.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
m_renderbuffer);
glViewport(0, 0, width, height);
m_simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(m_simpleProgram);
// Initialize the projection matrix.
ApplyOrtho(2, 3);
// Initialize rotation animation state.
OnRotate(DeviceOrientationPortrait);
m_desiredAngle = 0;
m_currentAngle = m_desiredAngle;
}
// 參考 Page 60 of iphone 3D program
void RenderingEngine2::ApplyOrtho(float maxX, float maxY) const
{
float a = 1.0f / maxX;
float b = 1.0f / maxY;
float ortho[16] = {
a, 0, 0, 0,
0, b, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
};
GLint projectionUniform = glGetUniformLocation(m_simpleProgram, "Projection");
glUniformMatrix4fv(projectionUniform, 1, 0, &ortho[0]);
}
void RenderingEngine2::ApplyRotation(float degrees) const
{
float radians = degrees * 3.14159f / 180.0f;
float s = std::sin(radians);
float c = std::cos(radians);
float zRotation[16] = {
c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
glUniformMatrix4fv(modelviewUniform, 1, 0, &zRotation[0]);
}
void RenderingEngine2::Render() const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT);
ApplyRotation(m_currentAngle);
GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");
GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &Vertices[0].Position[0];
const GLvoid* pColors = &Vertices[0].Color[0];
glVertexAttribPointer(positionSlot, 2, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
GLsizei vertexCount = sizeof(Vertices) / sizeof(Vertex);
glDrawArrays(GL_TRIANGLES, 0, vertexCount);
glDisableVertexAttribArray(positionSlot);
glDisableVertexAttribArray(colorSlot);
}
float RenderingEngine2::RotationDirection() const
{
float delta = 360 - m_currentAngle;
if (delta == 0 || delta == 360)
return 0;
bool counterclockwise = ((delta > 0 && delta <= 180) || (delta < 360 && delta > 180));
return counterclockwise ? +1 : -1;
}
void RenderingEngine2::UpdateAnimation(float timeStep)
{
float direction = RotationDirection();
if (direction == 0 ) {
//m_currentAngle =0;
return;
}
printf("direction = %f \n", direction);
printf("print old m_currentAngle = %f \n", m_currentAngle);
printf("print m_currentAngle = %f \n", m_currentAngle);
// Normalize the angle to [0, 360)
if (m_currentAngle >= 360)
m_currentAngle -= 360;
else if (m_currentAngle < 0)
m_currentAngle += 360;
if (m_currentAngle >= 1 && m_currentAngle <= 180)
m_currentAngle = m_currentAngle - (m_currentAngle/20);
else if (m_currentAngle > 180 && m_currentAngle < 359)
m_currentAngle = m_currentAngle + ((360- m_currentAngle )/20);
else {
m_currentAngle = 0;
m_desiredAngle =0;
}
float new_direction = RotationDirection();
if ((m_desiredAngle > 1) && (m_desiredAngle < 180.1))
m_desiredAngle = m_desiredAngle - 0.0005;
else if ((m_desiredAngle < 359) && (m_desiredAngle > 180))
m_desiredAngle = m_desiredAngle + 0.0005;
else {
m_desiredAngle = 0;
}
printf("print m_desiredAngle = %f \n", m_desiredAngle);
}
void RenderingEngine2::OnRotate(DeviceOrientation2 orientation) // DeviceOrientation
{
float angle = 0;
switch (orientation) {
case DeviceOrientationLandscapeLeft:
angle = 90;
break;
case DeviceOrientationPortraitUpsideDown:
angle = 180;
break;
case DeviceOrientationLandscapeRight:
angle = 270;
break;
default:
angle = 0;
break;
}
//m_desiredAngle = angle;
m_currentAngle = angle;
}
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;
}
8. GLView.h 作為OPENGLES載體的物件定義
#import <UIKit/UIKit.h>
#import "IRenderingEngine.hpp"
#import <QuartzCore/QuartzCore.h>
@interface GLView : UIView {
EAGLContext* m_context;
IRenderingEngine* m_renderingEngine;
float m_timestamp;
@public
BOOL ForceES1 ;
}
- (void) drawView: (CADisplayLink*) displayLink;
- (void) didRotate: (NSNotification*) notification;
- (void) setRender;
@end
9. GLView.mm 作為OPENGLES載體的設定主程式
#import "GLView.h"
#import <OpenGLES/ES2/gl.h> // <-- for GL_RENDERBUFFER only
@implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id) initWithFrame:(CGRect)frame
{
ForceES1 = FALSE;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self setRender];
m_timestamp = CACurrentMediaTime();
CADisplayLink* displayLink;
displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(drawView:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
return self;
}
- (void) didRotate: (NSNotification*) notification
{
// 手機旋轉時,讓箭頭也一起旋轉
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
m_renderingEngine->OnRotate((DeviceOrientation2) orientation); // DeviceOrientation
[self drawView: nil];
}
- (void) drawView: (CADisplayLink*) displayLink
{
if (displayLink != nil) {
float elapsedSeconds = displayLink.timestamp - m_timestamp;
m_timestamp = displayLink.timestamp;
m_renderingEngine->UpdateAnimation(elapsedSeconds);
//NSLog(@"elapsedSeconds = %f", elapsedSeconds);
}
m_renderingEngine->Render();
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void) setRender
{
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
eaglLayer.opaque = YES;
EAGLRenderingAPI api;
if (ForceES1 == NO){
api = kEAGLRenderingAPIOpenGLES2;
m_context = [[EAGLContext alloc] initWithAPI:api];
}
else{
api = kEAGLRenderingAPIOpenGLES1;
m_context = [[EAGLContext alloc] initWithAPI:api];
}
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
//[self release];
return ;
}
if (api == kEAGLRenderingAPIOpenGLES1) {
NSLog(@"Using OpenGL ES 1.1");
m_renderingEngine = CreateRenderer1();
} else {
NSLog(@"Using OpenGL ES 2.0");
m_renderingEngine = CreateRenderer2();
}
[m_context
renderbufferStorage:GL_RENDERBUFFER
fromDrawable: eaglLayer];
m_renderingEngine->Initialize(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
[self drawView: nil];
}
@end
10. mainViewController.h
#import <UIKit/UIKit.h>
#import "GLView.h"
@interface mainViewController : UIViewController
@property (strong, nonatomic) IBOutlet GLView *controllerView;
@property (strong, nonatomic) IBOutlet UIButton *ESBtn;
@end
11. mainViewController.mm ,此處副檔名要改成mm,因為連結的GLView.h含有C++的程式碼。
#import "mainViewController.h"
@interface mainViewController ()
@end
@implementation mainViewController
@synthesize controllerView;
BOOL oldForceES1;
@synthesize ESBtn;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
oldForceES1 = false;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)ChangeEngine:(id)sender {
oldForceES1 = ~oldForceES1;
controllerView->ForceES1 = oldForceES1;
[controllerView setRender];
if (oldForceES1)
[self.ESBtn setTitle:@"ES1 MODE" forState:UIControlStateNormal];
else
[self.ESBtn setTitle:@"ES2 MODE" forState:UIControlStateNormal];
}
@end
12. 結果圖





沒有留言:
張貼留言