iphone 3D Programm書上第二章的中文翻譯網址如下,可瞭解本例的細節。
http://blog.csdn.net/favormm/article/details/6920318
1. 先開一個專案
2. 加入所需 framework以及所新增的檔名,如下圖
3. 因為改成使用動態模式,因此storyboard內的元件一律清空。
4. 先新增GLSL的檔案
frag.glsl
const char* SimpleFragmentShader = STRINGIFY(
varying lowp vec4 DestinationColor;
void main()
{
gl_FragColor = DestinationColor;
}
);
vertex.glsl
const char* SimpleVertexShader = STRINGIFY(
attribute vec4 Position;
attribute vec4 SourceColor;
varying vec4 DestinationColor; // value output to frag.glsl
uniform mat4 Projection;
uniform mat4 Modelview;
void main()
{
DestinationColor = SourceColor;
gl_Position = Projection * Modelview * Position;
}
);
5. 將三個數學運算的檔案加入
Quaternion.hpp
#include "Matrix.hpp"
template <typename T>
struct QuaternionT {
T x;
T y;
T z;
T w;
QuaternionT();
QuaternionT(T x, T y, T z, T w);
QuaternionT<T> Slerp(T mu, const QuaternionT<T>& q) const;
QuaternionT<T> Rotated(const QuaternionT<T>& b) const;
QuaternionT<T> Scaled(T scale) const;
T Dot(const QuaternionT<T>& q) const;
Matrix3<T> ToMatrix() const;
Vector4<T> ToVector() const;
QuaternionT<T> operator-(const QuaternionT<T>& q) const;
QuaternionT<T> operator+(const QuaternionT<T>& q) const;
bool operator==(const QuaternionT<T>& q) const;
bool operator!=(const QuaternionT<T>& q) const;
void Normalize();
void Rotate(const QuaternionT<T>& q);
static QuaternionT<T> CreateFromVectors(const Vector3<T>& v0, const Vector3<T>& v1);
static QuaternionT<T> CreateFromAxisAngle(const Vector3<T>& axis, float radians);
};
template <typename T>
inline QuaternionT<T>::QuaternionT() : x(0), y(0), z(0), w(1)
{
}
template <typename T>
inline QuaternionT<T>::QuaternionT(T x, T y, T z, T w) : x(x), y(y), z(z), w(w)
{
}
// Ken Shoemake's famous method.
template <typename T>
inline QuaternionT<T> QuaternionT<T>::Slerp(T t, const QuaternionT<T>& v1) const
{
const T epsilon = 0.0005f;
T dot = Dot(v1);
if (dot > 1 - epsilon) {
QuaternionT<T> result = v1 + (*this - v1).Scaled(t);
result.Normalize();
return result;
}
if (dot < 0)
dot = 0;
if (dot > 1)
dot = 1;
T theta0 = std::acos(dot);
T theta = theta0 * t;
QuaternionT<T> v2 = (v1 - Scaled(dot));
v2.Normalize();
QuaternionT<T> q = Scaled(std::cos(theta)) + v2.Scaled(std::sin(theta));
q.Normalize();
return q;
}
template <typename T>
inline QuaternionT<T> QuaternionT<T>::Rotated(const QuaternionT<T>& b) const
{
QuaternionT<T> q;
q.w = w * b.w - x * b.x - y * b.y - z * b.z;
q.x = w * b.x + x * b.w + y * b.z - z * b.y;
q.y = w * b.y + y * b.w + z * b.x - x * b.z;
q.z = w * b.z + z * b.w + x * b.y - y * b.x;
q.Normalize();
return q;
}
template <typename T>
inline QuaternionT<T> QuaternionT<T>::Scaled(T s) const
{
return QuaternionT<T>(x * s, y * s, z * s, w * s);
}
template <typename T>
inline T QuaternionT<T>::Dot(const QuaternionT<T>& q) const
{
return x * q.x + y * q.y + z * q.z + w * q.w;
}
template <typename T>
inline Matrix3<T> QuaternionT<T>::ToMatrix() const
{
const T s = 2;
T xs, ys, zs;
T wx, wy, wz;
T xx, xy, xz;
T yy, yz, zz;
xs = x * s; ys = y * s; zs = z * s;
wx = w * xs; wy = w * ys; wz = w * zs;
xx = x * xs; xy = x * ys; xz = x * zs;
yy = y * ys; yz = y * zs; zz = z * zs;
Matrix3<T> m;
m.x.x = 1 - (yy + zz); m.y.x = xy - wz; m.z.x = xz + wy;
m.x.y = xy + wz; m.y.y = 1 - (xx + zz); m.z.y = yz - wx;
m.x.z = xz - wy; m.y.z = yz + wx; m.z.z = 1 - (xx + yy);
return m;
}
template <typename T>
inline Vector4<T> QuaternionT<T>::ToVector() const
{
return Vector4<T>(x, y, z, w);
}
template <typename T>
QuaternionT<T> QuaternionT<T>::operator-(const QuaternionT<T>& q) const
{
return QuaternionT<T>(x - q.x, y - q.y, z - q.z, w - q.w);
}
template <typename T>
QuaternionT<T> QuaternionT<T>::operator+(const QuaternionT<T>& q) const
{
return QuaternionT<T>(x + q.x, y + q.y, z + q.z, w + q.w);
}
template <typename T>
bool QuaternionT<T>::operator==(const QuaternionT<T>& q) const
{
return x == q.x && y == q.y && z == q.z && w == q.w;
}
template <typename T>
bool QuaternionT<T>::operator!=(const QuaternionT<T>& q) const
{
return !(*this == q);
}
// Compute the quaternion that rotates from a to b, avoiding numerical instability.
// Taken from "The Shortest Arc Quaternion" by Stan Melax in "Game Programming Gems".
template <typename T>
inline QuaternionT<T> QuaternionT<T>::CreateFromVectors(const Vector3<T>& v0, const Vector3<T>& v1)
{
if (v0 == -v1)
return QuaternionT<T>::CreateFromAxisAngle(vec3(1, 0, 0), Pi);
Vector3<T> c = v0.Cross(v1);
T d = v0.Dot(v1);
T s = std::sqrt((1 + d) * 2);
QuaternionT<T> q;
q.x = c.x / s;
q.y = c.y / s;
q.z = c.z / s;
q.w = s / 2.0f;
return q;
}
template <typename T>
inline QuaternionT<T> QuaternionT<T>::CreateFromAxisAngle(const Vector3<T>& axis, float radians)
{
QuaternionT<T> q;
q.w = std::cos(radians / 2);
q.x = q.y = q.z = std::sin(radians / 2);
q.x *= axis.x;
q.y *= axis.y;
q.z *= axis.z;
return q;
}
template <typename T>
inline void QuaternionT<T>::Normalize()
{
*this = Scaled(1 / std::sqrt(Dot(*this)));
}
template <typename T>
inline void QuaternionT<T>::Rotate(const QuaternionT<T>& q2)
{
QuaternionT<T> q;
QuaternionT<T>& q1 = *this;
q.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
q.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
q.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
q.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
q.Normalize();
*this = q;
}
typedef QuaternionT<float> Quaternion;
---------------------------------------------------------
Matrix.hpp
#include "Vector.hpp"
template <typename T>
struct Matrix2 {
Matrix2()
{
x.x = 1; x.y = 0;
y.x = 0; y.y = 1;
}
Matrix2(const T* m)
{
x.x = m[0]; x.y = m[1];
y.x = m[2]; y.y = m[3];
}
vec2 x;
vec2 y;
};
template <typename T>
struct Matrix3 {
Matrix3()
{
x.x = 1; x.y = 0; x.z = 0;
y.x = 0; y.y = 1; y.z = 0;
z.x = 0; z.y = 0; z.z = 1;
}
Matrix3(const T* m)
{
x.x = m[0]; x.y = m[1]; x.z = m[2];
y.x = m[3]; y.y = m[4]; y.z = m[5];
z.x = m[6]; z.y = m[7]; z.z = m[8];
}
Matrix3 Transposed() const
{
Matrix3 m;
m.x.x = x.x; m.x.y = y.x; m.x.z = z.x;
m.y.x = x.y; m.y.y = y.y; m.y.z = z.y;
m.z.x = x.z; m.z.y = y.z; m.z.z = z.z;
return m;
}
const T* Pointer() const
{
return &x.x;
}
vec3 x;
vec3 y;
vec3 z;
};
template <typename T>
struct Matrix4 {
Matrix4()
{
x.x = 1; x.y = 0; x.z = 0; x.w = 0;
y.x = 0; y.y = 1; y.z = 0; y.w = 0;
z.x = 0; z.y = 0; z.z = 1; z.w = 0;
w.x = 0; w.y = 0; w.z = 0; w.w = 1;
}
Matrix4(const Matrix3<T>& m)
{
x.x = m.x.x; x.y = m.x.y; x.z = m.x.z; x.w = 0;
y.x = m.y.x; y.y = m.y.y; y.z = m.y.z; y.w = 0;
z.x = m.z.x; z.y = m.z.y; z.z = m.z.z; z.w = 0;
w.x = 0; w.y = 0; w.z = 0; w.w = 1;
}
Matrix4(const T* m)
{
x.x = m[0]; x.y = m[1]; x.z = m[2]; x.w = m[3];
y.x = m[4]; y.y = m[5]; y.z = m[6]; y.w = m[7];
z.x = m[8]; z.y = m[9]; z.z = m[10]; z.w = m[11];
w.x = m[12]; w.y = m[13]; w.z = m[14]; w.w = m[15];
}
Matrix4 operator * (const Matrix4& b) const
{
Matrix4 m;
m.x.x = x.x * b.x.x + x.y * b.y.x + x.z * b.z.x + x.w * b.w.x;
m.x.y = x.x * b.x.y + x.y * b.y.y + x.z * b.z.y + x.w * b.w.y;
m.x.z = x.x * b.x.z + x.y * b.y.z + x.z * b.z.z + x.w * b.w.z;
m.x.w = x.x * b.x.w + x.y * b.y.w + x.z * b.z.w + x.w * b.w.w;
m.y.x = y.x * b.x.x + y.y * b.y.x + y.z * b.z.x + y.w * b.w.x;
m.y.y = y.x * b.x.y + y.y * b.y.y + y.z * b.z.y + y.w * b.w.y;
m.y.z = y.x * b.x.z + y.y * b.y.z + y.z * b.z.z + y.w * b.w.z;
m.y.w = y.x * b.x.w + y.y * b.y.w + y.z * b.z.w + y.w * b.w.w;
m.z.x = z.x * b.x.x + z.y * b.y.x + z.z * b.z.x + z.w * b.w.x;
m.z.y = z.x * b.x.y + z.y * b.y.y + z.z * b.z.y + z.w * b.w.y;
m.z.z = z.x * b.x.z + z.y * b.y.z + z.z * b.z.z + z.w * b.w.z;
m.z.w = z.x * b.x.w + z.y * b.y.w + z.z * b.z.w + z.w * b.w.w;
m.w.x = w.x * b.x.x + w.y * b.y.x + w.z * b.z.x + w.w * b.w.x;
m.w.y = w.x * b.x.y + w.y * b.y.y + w.z * b.z.y + w.w * b.w.y;
m.w.z = w.x * b.x.z + w.y * b.y.z + w.z * b.z.z + w.w * b.w.z;
m.w.w = w.x * b.x.w + w.y * b.y.w + w.z * b.z.w + w.w * b.w.w;
return m;
}
Matrix4& operator *= (const Matrix4& b)
{
Matrix4 m = *this * b;
return (*this = m);
}
Matrix4 Transposed() const
{
Matrix4 m;
m.x.x = x.x; m.x.y = y.x; m.x.z = z.x; m.x.w = w.x;
m.y.x = x.y; m.y.y = y.y; m.y.z = z.y; m.y.w = w.y;
m.z.x = x.z; m.z.y = y.z; m.z.z = z.z; m.z.w = w.z;
m.w.x = x.w; m.w.y = y.w; m.w.z = z.w; m.w.w = w.w;
return m;
}
Matrix3<T> ToMat3() const
{
Matrix3<T> m;
m.x.x = x.x; m.y.x = y.x; m.z.x = z.x;
m.x.y = x.y; m.y.y = y.y; m.z.y = z.y;
m.x.z = x.z; m.y.z = y.z; m.z.z = z.z;
return m;
}
const T* Pointer() const
{
return &x.x;
}
static Matrix4<T> Identity()
{
return Matrix4();
}
static Matrix4<T> Translate(T x, T y, T z)
{
Matrix4 m;
m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
return m;
}
static Matrix4<T> Scale(T s)
{
Matrix4 m;
m.x.x = s; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = s; m.y.z = 0; m.y.w = 0;
m.z.x = 0; m.z.y = 0; m.z.z = s; m.z.w = 0;
m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
return m;
}
static Matrix4<T> Rotate(T degrees)
{
T radians = degrees * 3.14159f / 180.0f;
T s = std::sin(radians);
T c = std::cos(radians);
Matrix4 m = Identity();
m.x.x = c; m.x.y = s;
m.y.x = -s; m.y.y = c;
return m;
}
static Matrix4<T> Rotate(T degrees, const vec3& axis)
{
T radians = degrees * 3.14159f / 180.0f;
T s = std::sin(radians);
T c = std::cos(radians);
Matrix4 m = Identity();
m.x.x = c + (1 - c) * axis.x * axis.x;
m.x.y = (1 - c) * axis.x * axis.y - axis.z * s;
m.x.z = (1 - c) * axis.x * axis.z + axis.y * s;
m.y.x = (1 - c) * axis.x * axis.y + axis.z * s;
m.y.y = c + (1 - c) * axis.y * axis.y;
m.y.z = (1 - c) * axis.y * axis.z - axis.x * s;
m.z.x = (1 - c) * axis.x * axis.z - axis.y * s;
m.z.y = (1 - c) * axis.y * axis.z + axis.x * s;
m.z.z = c + (1 - c) * axis.z * axis.z;
return m;
}
static Matrix4<T> Frustum(T left, T right, T bottom, T top, T near, T far)
{
T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;
}
vec4 x;
vec4 y;
vec4 z;
vec4 w;
};
typedef Matrix2<float> mat2;
typedef Matrix3<float> mat3;
typedef Matrix4<float> mat4;
---------------------------------------------------------
Vector.hpp
#include <cmath>
const float Pi = 4 * std::atan(1.0f);
const float TwoPi = 2 * Pi;
template <typename T>
struct Vector2 {
Vector2() {}
Vector2(T x, T y) : x(x), y(y) {}
T Dot(const Vector2& v) const
{
return x * v.x + y * v.y;
}
Vector2 operator+(const Vector2& v) const
{
return Vector2(x + v.x, y + v.y);
}
Vector2 operator-(const Vector2& v) const
{
return Vector2(x - v.x, y - v.y);
}
Vector2 operator/(float s) const
{
return Vector2(x / s, y / s);
}
Vector2 operator*(float s) const
{
return Vector2(x * s, y * s);
}
void Normalize()
{
float s = 1.0f / Length();
x *= s;
y *= s;
}
Vector2 Normalized() const
{
Vector2 v = *this;
v.Normalize();
return v;
}
T LengthSquared() const
{
return x * x + y * y;
}
T Length() const
{
return sqrt(LengthSquared());
}
operator Vector2<float>() const
{
return Vector2<float>(x, y);
}
bool operator==(const Vector2& v) const
{
return x == v.x && y == v.y;
}
Vector2 Lerp(float t, const Vector2& v) const
{
return Vector2(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t);
}
template <typename P>
P* Write(P* pData)
{
Vector2* pVector = (Vector2*) pData;
*pVector++ = *this;
return (P*) pVector;
}
T x;
T y;
};
template <typename T>
struct Vector3 {
Vector3() {}
Vector3(T x, T y, T z) : x(x), y(y), z(z) {}
void Normalize()
{
float s = 1.0f / std::sqrt(x * x + y * y + z * z);
x *= s;
y *= s;
z *= s;
}
Vector3 Normalized() const
{
Vector3 v = *this;
v.Normalize();
return v;
}
Vector3 Cross(const Vector3& v) const
{
return Vector3(y * v.z - z * v.y,
z * v.x - x * v.z,
x * v.y - y * v.x);
}
T Dot(const Vector3& v) const
{
return x * v.x + y * v.y + z * v.z;
}
Vector3 operator+(const Vector3& v) const
{
return Vector3(x + v.x, y + v.y, z + v.z);
}
void operator+=(const Vector3& v)
{
x += v.x;
y += v.y;
z += v.z;
}
void operator-=(const Vector3& v)
{
x -= v.x;
y -= v.y;
z -= v.z;
}
void operator/=(T s)
{
x /= s;
y /= s;
z /= s;
}
Vector3 operator-(const Vector3& v) const
{
return Vector3(x - v.x, y - v.y, z - v.z);
}
Vector3 operator-() const
{
return Vector3(-x, -y, -z);
}
Vector3 operator*(T s) const
{
return Vector3(x * s, y * s, z * s);
}
Vector3 operator/(T s) const
{
return Vector3(x / s, y / s, z / s);
}
bool operator==(const Vector3& v) const
{
return x == v.x && y == v.y && z == v.z;
}
Vector3 Lerp(float t, const Vector3& v) const
{
return Vector3(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t,
z * (1 - t) + v.z * t);
}
const T* Pointer() const
{
return &x;
}
template <typename P>
P* Write(P* pData)
{
Vector3<T>* pVector = (Vector3<T>*) pData;
*pVector++ = *this;
return (P*) pVector;
}
T x;
T y;
T z;
};
template <typename T>
struct Vector4 {
Vector4() {}
Vector4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
T Dot(const Vector4& v) const
{
return x * v.x + y * v.y + z * v.z + w * v.w;
}
Vector4 Lerp(float t, const Vector4& v) const
{
return Vector4(x * (1 - t) + v.x * t,
y * (1 - t) + v.y * t,
z * (1 - t) + v.z * t,
w * (1 - t) + v.w * t);
}
const T* Pointer() const
{
return &x;
}
T x;
T y;
T z;
T w;
};
typedef Vector2<bool> bvec2;
typedef Vector2<int> ivec2;
typedef Vector3<int> ivec3;
typedef Vector4<int> ivec4;
typedef Vector2<float> vec2;
typedef Vector3<float> vec3;
typedef Vector4<float> vec4;
6. 加入IRenderingEngine.hpp的內容
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();
// 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 ~IRenderingEngine() {}
};
7. RenderingEngine1.cpp
#include <OpenGLES/ES1/gl.h>#include <OpenGLES/ES1/glext.h>
#include "IRenderingEngine.hpp"
#include "Quaternion.hpp"
#include <vector>
static const float AnimationDuration = 0.25f;
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
struct Animation {
Quaternion Start;
Quaternion End;
Quaternion Current;
float Elapsed;
float Duration;
};
class RenderingEngine1 : public IRenderingEngine {
public:
RenderingEngine1();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep);
void OnRotate(DeviceOrientation newOrientation);
private:
vector<Vertex> m_cone;
vector<Vertex> m_disk;
Animation m_animation;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
};
IRenderingEngine* CreateRenderer1()
{
return new RenderingEngine1();
}
RenderingEngine1::RenderingEngine1()
{
// 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)
{
const float coneRadius = 0.5f;
const float coneHeight = 2.866f; //高度加高
const int coneSlices = 40;
{
// Allocate space for the cone vertices.
m_cone.resize((coneSlices + 1) * 2);
// Initialize the vertices of the triangle strip.,使用 GL_TRIANGLE_STRIP
vector<Vertex>::iterator vertex = m_cone.begin();
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_cone.end(); theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness*2, brightness, 0.5); // 投射的顏色改成偏綠色。
// 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++;
}
}
{
// Allocate space for the disk vertices.
m_disk.resize(coneSlices + 2);
// Initialize the center vertex of the triangle fan.
vector<Vertex>::iterator vertex = m_disk.begin();
vertex->Color = vec4(3, 0.75, 0.75, 1); // 底部顏色改掉
vertex->Position.x = 0;
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = 0;
vertex++;
// Initialize the rim vertices of the triangle fan.
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_disk.end(); theta += dtheta) {
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex++;
}
}
// 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
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
mat4 rotation(m_animation.Current.ToMatrix());
glMultMatrixf(rotation.Pointer());
// Draw the cone.
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &m_cone[0].Position.x);
glColorPointer(4, GL_FLOAT, sizeof(Vertex), &m_cone[0].Color.x);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_cone.size());
// Draw the disk that caps off the base of the cone.
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &m_disk[0].Position.x);
glColorPointer(4, GL_FLOAT, sizeof(Vertex), &m_disk[0].Color.x);
glDrawArrays(GL_TRIANGLE_FAN, 0, m_disk.size());
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopMatrix();
}
void RenderingEngine1::UpdateAnimation(float timeStep)
{
if (m_animation.Current == m_animation.End)
return;
m_animation.Elapsed += timeStep;
if (m_animation.Elapsed >= AnimationDuration) {
m_animation.Current = m_animation.End;
} else {
float mu = m_animation.Elapsed / AnimationDuration;
m_animation.Current = m_animation.Start.Slerp(mu, m_animation.End);
}
}
void RenderingEngine1::OnRotate(DeviceOrientation orientation)
{
vec3 direction;
switch (orientation) {
case DeviceOrientationUnknown:
case DeviceOrientationPortrait:
direction = vec3(0, 1, 0);
break;
case DeviceOrientationPortraitUpsideDown:
direction = vec3(0, -1, 0);
break;
case DeviceOrientationFaceDown:
direction = vec3(0, 0, -1);
break;
case DeviceOrientationFaceUp:
direction = vec3(0, 0, 1);
break;
case DeviceOrientationLandscapeLeft:
direction = vec3(+1, 0, 0);
break;
case DeviceOrientationLandscapeRight:
direction = vec3(-1, 0, 0);
break;
}
m_animation.Elapsed = 0;
m_animation.Start = m_animation.Current = m_animation.End;
m_animation.End = Quaternion::CreateFromVectors(vec3(0, 1, 0), direction);
}
8. RenderingEngine2.cpp
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "IRenderingEngine.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <iostream>
#define STRINGIFY(A) #A
#include "./frag.glsl"
#include "./vertex.glsl"
static const float AnimationDuration = 0.25f;
using namespace std;
struct Vertex {
vec3 Position;
vec4 Color;
};
struct Animation {
Quaternion Start;
Quaternion End;
Quaternion Current;
float Elapsed;
float Duration;
};
class RenderingEngine2 : public IRenderingEngine {
public:
RenderingEngine2();
void Initialize(int width, int height);
void Render() const;
void UpdateAnimation(float timeStep);
void OnRotate(DeviceOrientation newOrientation);
private:
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
vector<Vertex> m_cone;
vector<Vertex> m_disk;
Animation m_animation;
GLuint m_simpleProgram;
GLuint m_framebuffer;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
};
IRenderingEngine* CreateRenderer2()
{
return new RenderingEngine2();
}
RenderingEngine2::RenderingEngine2()
{
// 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)
{
const float coneRadius = 0.5f;
const float coneHeight = 1.866f;
const int coneSlices = 40;
{
// Allocate space for the cone vertices.
m_cone.resize((coneSlices + 1) * 2); // 82
// Initialize the vertices of the triangle strip. 三角型序列,產生锥顶点,使用GL_TRIANGLE_STRIP
vector<Vertex>::iterator vertex = m_cone.begin(); // 定義一個迭代的vertex變數,並指定為m_cone的第一個變數
const float dtheta = TwoPi / coneSlices; // 一個圓切成40點,每一個角度
for (float theta = 0; vertex != m_cone.end(); theta += dtheta) { // 圓上切點
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness, brightness, 1);//指定一個color的Vec4,給後面的vertex設定顏色
// Apex(外切,頂點) vertex,指定偶數Vertex的內容值 (0, 2, 4...)
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim(圓環,底邊的弧) vertex,指定奇數Vertex的內容值 (1, 3, 5...)
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
} // 將m_cone所有點的內容值全都設定好。
{
// Allocate space for the disk vertices. 生成锥底顶点,使用 GL_TRIANGLE_FAN模式
m_disk.resize(coneSlices + 2);
// Initialize the center vertex of the triangle fan. 底部中心點,
vector<Vertex>::iterator vertex = m_disk.begin();
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = 0;
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = 0;
vertex++;
// Initialize the rim vertices of the triangle fan.
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_disk.end(); theta += dtheta) {
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex++;
}
}
// Create the depth buffer. 为深度缓冲生成一个id,绑定它,并为之分配存储空间。
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.
// 为缓冲对象生成id,绑定之,并把深度与颜色缓冲用glFramebufferRenderbufferOES依附于它。
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);
// Set up some GL state.
glViewport(0, 0, width, height); //设置viewport的左下角,长,宽属性。
glEnable(GL_DEPTH_TEST); // 为3D场景开启深度测试
// Build the GLSL program.
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");
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
mat4 rotation(m_animation.Current.ToMatrix());
mat4 translation = mat4::Translate(0, 0, -7);
// Set the model-view matrix.
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
mat4 modelviewMatrix = rotation * translation;
glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
// Draw the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_cone[0].Position.x;
const GLvoid* pColors = &m_cone[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_cone.size());
}
// Draw the disk that caps off the base of the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_disk[0].Position.x;
const GLvoid* pColors = &m_disk[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_FAN, 0, m_disk.size());
}
glDisableVertexAttribArray(positionSlot);
glDisableVertexAttribArray(colorSlot);
}
void RenderingEngine2::UpdateAnimation(float timeStep)
{
if (m_animation.Current == m_animation.End)
return;
m_animation.Elapsed += timeStep;
if (m_animation.Elapsed >= AnimationDuration) {
m_animation.Current = m_animation.End;
} else {
float mu = m_animation.Elapsed / AnimationDuration;
m_animation.Current = m_animation.Start.Slerp(mu, m_animation.End);
}
}
void RenderingEngine2::OnRotate(DeviceOrientation orientation)
{
vec3 direction;
switch (orientation) {
case DeviceOrientationUnknown:
case DeviceOrientationPortrait:
direction = vec3(0, 1, 0);
break;
case DeviceOrientationPortraitUpsideDown:
direction = vec3(0, -1, 0);
break;
case DeviceOrientationFaceDown:
direction = vec3(0, 0, -1);
break;
case DeviceOrientationFaceUp:
direction = vec3(0, 0, 1);
break;
case DeviceOrientationLandscapeLeft:
direction = vec3(+1, 0, 0);
break;
case DeviceOrientationLandscapeRight:
direction = vec3(-1, 0, 0);
break;
}
m_animation.Elapsed = 0;
m_animation.Start = m_animation.Current = m_animation.End;
m_animation.End = Quaternion::CreateFromVectors(vec3(0, 1, 0), direction);
}
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;
}
9. GLView.h 作為OPENGLES載體的物件定義
#import <UIKit/UIKit.h>#import "IRenderingEngine.hpp"
#import <QuartzCore/QuartzCore.h>
@interface GLView : UIView
{
///@private
EAGLContext* m_context;
IRenderingEngine* m_renderingEngine;
float m_timestamp;
@public
BOOL ForceES1 ;
}
- (void) drawView: (CADisplayLink*) displayLink;
- (void) didRotate: (NSNotification*) notification;
- (id) initSet:(CGRect) frame;
@end
10. GLView.mm 作為OPENGLES載體的設定主程式
#import "GLView.h"
@implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id) initWithFrame: (CGRect) frame
{
ForceES1 = NO;
if (self = [super initWithFrame:frame]) {
if ([self initSet:frame] == nil)
return nil;
}
return self;
}
- (id) initSet:(CGRect) frame
{
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) super.layer;
eaglLayer.opaque = YES;
EAGLRenderingAPI api;
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]) {
//[self release];
return nil;
}
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(frame), CGRectGetHeight(frame));
[self drawView: nil];
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((DeviceOrientation) orientation);
[self drawView: nil];
}
- (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];
}
@end
11. mainViewController.h
#import <UIKit/UIKit.h>
#import "GLView.h"
@interface mainViewController : UIViewController
{
UIWindow* m_window;
GLView *controllView;
}
@end
12. mainViewController.mm ,此處副檔名要改成mm,因為連結的GLView.h含有C++的程式碼。 所有顯示元件都是動態產生的。
#import "mainViewController.h"
@interface mainViewController ()
@end
@implementation mainViewController
{
UIButton *swBtn;
}
BOOL mode1;
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
m_window = [[UIWindow alloc] initWithFrame: screenBounds];
controllView = [[GLView alloc] initWithFrame: screenBounds];
[self setButtonInterface];
[m_window addSubview: controllView];
[controllView addSubview:swBtn];
[m_window makeKeyAndVisible];
mode1 = 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這兩個字
}
- (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];
}
@end



