PVRTC是Apple推薦的texture壓縮格式,因此在iphone 3D Programming中的texture format重點就在PVRTC,以下是將書中範例轉成iPad的實作過程
1. 全新的專案
2.將texture及相關檔案放入,其中cmath及shaders與之前的例子相同,直接沿用。mainViewController相關檔案也是沿用就可以了。
3. PowerVR目錄主要是放置PVRTC的Header及相關定義,
由於需要其PVRTC的textheader來記錄讀取PVRTC的資料,來源有兩個一個從Apple: http://developer.apple.com/library/ios/#samplecode/PVRTextureLoader/Listings/Classes_PVRTexture_m.html
一個從安裝的Imagination的SDK: 位置在/Users/Shared/Imagination/PowerVR/GraphicsSDK/SDK_3.1/Tools/PVRTTexture.h 及PVRTGlobal.h
4.ApplicationEngine.cpp是此例子的重點,記錄要用到的texture檔,及讀取檔案的方法
#include "Interfaces.hpp"#include "ParametricSurface.hpp"
using namespace std;
const string TextureFiles[] = {
"Grasshopper.png",
/* "Utopia4444.pvr",
"Grasshopper565.pvr",
"Luma8.png",
"LumaAlpha8.png",
"Rgb8.png",
"Rgba8.png",
"LetterA.png",
"Utopia.png",*/
"Astronomy.jpg",
"Utopia5551.pvr", //使用Imagination的tool產生的
"Astronomy4444.pvr", //使用Imagination的tool產生的
"girl3.pvr", // RGBA565, use texturetool
"pets3.pvr", // use texturetool
};
class ApplicationEngine : public IApplicationEngine {
public:
ApplicationEngine(IRenderingEngine* renderingEngine);
~ApplicationEngine();
void Initialize(int width, int height);
void OnFingerUp(ivec2 location) {}
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation) {}
void Render() const;
void UpdateAnimation(float dt);
private:
void LoadTexture();
ivec2 m_screenSize;
ivec2 m_centerPoint;
IRenderingEngine* m_renderingEngine;
int m_textureIndex;
float m_timer;
};
IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
{
return new ApplicationEngine(renderingEngine);
}
ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
m_renderingEngine(renderingEngine),
m_timer(0)
{
}
ApplicationEngine::~ApplicationEngine()
{
delete m_renderingEngine;
}
void ApplicationEngine::Initialize(int width, int height)
{
m_screenSize = ivec2(width, height);
//m_centerPoint = m_screenSize / 2;
vector<ISurface*> surfaces(1);
surfaces[0] = new Quad(2, 2);
m_renderingEngine->Initialize(surfaces); //先產生物件,後面再載入texture
delete surfaces[0];
m_textureIndex = 0;
LoadTexture(); // 載入texture
}
void ApplicationEngine::Render() const
{
vector<Visual> visuals(1);
visuals[0].Color = vec3(1, 1, 1);
visuals[0].LowerLeft = ivec2(-100, 0); // -160
//visuals[0].ViewportSize = ivec2(m_screenSize.x*2, m_screenSize.y);
visuals[0].ViewportSize = ivec2(m_screenSize.x, m_screenSize.y); // fixed viewPortSize for iPad
visuals[0].Orientation = Quaternion();
m_renderingEngine->Render(visuals);
}
// 這裡提供手指按下後,立刻輪換下一張圖檔的功能
void ApplicationEngine::OnFingerDown(ivec2 location)
{
m_textureIndex++;
if (m_textureIndex >= sizeof(TextureFiles) / sizeof(TextureFiles[0]))
m_textureIndex = 0; //超過就回到第一個檔案
LoadTexture();
}
void ApplicationEngine::UpdateAnimation(float dt)
{
m_timer += dt; // 每隔一段時間,就輪換一張圖檔
if (m_timer > 0.75f) { //時間必須大於0.75
m_timer = 0;
//OnFingerDown(ivec2(0, 0));
m_textureIndex++;
if (m_textureIndex >= sizeof(TextureFiles) / sizeof(TextureFiles[0]))
m_textureIndex = 0;
LoadTexture();
}
}
void ApplicationEngine::LoadTexture()
{
string filename = TextureFiles[m_textureIndex];
string suffix = ".pvr";
size_t i = filename.rfind(suffix);
if (i != string::npos && i == (filename.length() - suffix.length())) //最後檔尾為pvr檔的
m_renderingEngine->SetPvrTexture(filename); //處理PVRTC
else
m_renderingEngine->SetPngTexture(filename); // 處理一般的PNG/JPG檔
}
5. GLView.h與前例一樣,GLView.mm簡化許多
#import "GLView.h"@implementation GLView
+ (Class) layerClass
{
return [CAEAGLLayer class];
}
- (id) initWithFrame: (CGRect) frame
{
if (self = [super initWithFrame:frame])
{
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
eaglLayer.opaque = YES;
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
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;
}
m_resourceManager = CreateResourceManager();
if (api == kEAGLRenderingAPIOpenGLES1) {
NSLog(@"Using OpenGL ES 1.1");
m_renderingEngine = ES1::CreateRenderingEngine(m_resourceManager);
} else {
NSLog(@"Using OpenGL ES 2.0");
m_renderingEngine = ES2::CreateRenderingEngine(m_resourceManager);
}
m_applicationEngine = CreateApplicationEngine(m_renderingEngine);
[m_context
renderbufferStorage:GL_RENDERBUFFER
fromDrawable:eaglLayer];
int width = CGRectGetWidth(frame);
int height = CGRectGetHeight(frame);
m_applicationEngine->Initialize(width, height);
[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_applicationEngine->UpdateAnimation(elapsedSeconds);
}
m_applicationEngine->Render();
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}
//手指按下後
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
m_applicationEngine->OnFingerDown(ivec2(location.x, location.y));
}
@end
6. Interfaces.hpp是本例的另一個重點,定義texture格式變數
#pragma once#include "Vector.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <string>
using std::vector;
using std::string;
enum VertexFlags {
VertexFlagsNormals = 1 << 0,
VertexFlagsTexCoords = 1 << 1,
};
enum TextureFormat {
TextureFormatGray,
TextureFormatGrayAlpha,
TextureFormatRgb,
TextureFormatRgba,
TextureFormatPvrtcRgb2,
TextureFormatPvrtcRgba2,
TextureFormatPvrtcRgb4,
TextureFormatPvrtcRgba4,
TextureFormat565,
TextureFormat5551,
};
struct TextureDescription {
TextureFormat Format;
int BitsPerComponent;
ivec2 Size;
int MipCount;
};
struct IApplicationEngine {
virtual void Initialize(int width, int height) = 0;
virtual void Render() const = 0;
virtual void UpdateAnimation(float timeStep) = 0;
virtual void OnFingerUp(ivec2 location) = 0;
virtual void OnFingerDown(ivec2 location) = 0;
virtual void OnFingerMove(ivec2 oldLocation, ivec2 newLocation) = 0;
virtual ~IApplicationEngine() {}
};
struct ISurface {
virtual int GetVertexCount() const = 0;
virtual int GetLineIndexCount() const = 0;
virtual int GetTriangleIndexCount() const = 0;
virtual void GenerateVertices(vector<float>& vertices,
unsigned char flags = 0) const = 0;
virtual void GenerateLineIndices(vector<unsigned short>& indices) const = 0;
virtual void GenerateTriangleIndices(vector<unsigned short>& indices) const = 0;
virtual ~ISurface() {}
};
struct Visual {
vec3 Color;
ivec2 LowerLeft;
ivec2 ViewportSize;
Quaternion Orientation;
};
struct IRenderingEngine {
virtual void Initialize(const vector<ISurface*>& surfaces) = 0;
virtual void Render(const vector<Visual>& visuals) const = 0;
virtual void SetPngTexture(const string& file) const = 0;
virtual void SetPvrTexture(const string& file) const = 0;
virtual ~IRenderingEngine() {}
};
struct IResourceManager {
virtual string GetResourcePath() const = 0;
//virtual TextureDescription LoadPngImage(const string& filename) = 0;
virtual TextureDescription LoadPvrImage(const string& filename) = 0;
virtual TextureDescription LoadImage(const string& filename) = 0;
virtual TextureDescription GenerateCircle() = 0;
virtual void* GetImageData() = 0;
virtual void UnloadImage() = 0;
virtual ~IResourceManager() {}
};
IApplicationEngine* CreateApplicationEngine(IRenderingEngine*);
IResourceManager* CreateResourceManager();
namespace ES1 { IRenderingEngine* CreateRenderingEngine(IResourceManager*); }
namespace ES2 { IRenderingEngine* CreateRenderingEngine(IResourceManager*); }
7. RenderingEngine.ES1.cpp,由於著重在ES2,因此對ES1沒有做什麼修改。ES1與ES2讀取texture檔的方式一樣。
#include <OpenGLES/ES1/gl.h>#include <OpenGLES/ES1/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <iostream>
#include <assert.h>
using namespace std;
namespace ES1 {
struct Drawable {
GLuint VertexBuffer;
GLuint IndexBuffer;
int IndexCount;
};
class RenderingEngine : public IRenderingEngine {
public:
RenderingEngine(IResourceManager* resourceManager);
void Initialize(const vector<ISurface*>& surfaces);
void Render(const vector<Visual>& visuals) const;
void SetPngTexture(const string& name) const;
void SetPvrTexture(const string& file) const;
private:
vector<Drawable> m_drawables;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
mat4 m_translation;
IResourceManager* m_resourceManager;
mutable float m_offset;
};
IRenderingEngine* CreateRenderingEngine(IResourceManager* resourceManager)
{
return new RenderingEngine(resourceManager);
}
RenderingEngine::RenderingEngine(IResourceManager* resourceManager)
{
m_resourceManager = resourceManager;
m_offset = 0;
glGenRenderbuffersOES(1, &m_colorRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
}
void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
vector<ISurface*>::const_iterator surface;
for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
// Create the VBO for the vertices.
vector<float> vertices;
unsigned char vertexFlags = VertexFlagsTexCoords;
(*surface)->GenerateVertices(vertices, vertexFlags);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
vertices.size() * sizeof(vertices[0]),
&vertices[0],
GL_STATIC_DRAW);
// Create a new VBO for the indices if needed.
int indexCount = (*surface)->GetTriangleIndexCount();
GLuint indexBuffer;
if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
indexBuffer = m_drawables[0].IndexBuffer;
} else {
vector<GLushort> indices(indexCount);
(*surface)->GenerateTriangleIndices(indices);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indexCount * sizeof(GLushort),
&indices[0],
GL_STATIC_DRAW);
}
Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
m_drawables.push_back(drawable);
}
// Extract width and height from the color buffer.
int width, height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
GL_RENDERBUFFER_HEIGHT_OES, &height);
// Create a depth buffer that has the same size as the color 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.
GLuint framebuffer;
glGenFramebuffersOES(1, &framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 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);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
// Set up various GL state.
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Set up the texture state.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Set up the material properties.
vec4 specular(0.5f, 0.5f, 0.5f, 1);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular.Pointer());
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f);
m_translation = mat4::Translate(0, 0, -7);
}
void RenderingEngine::Render(const vector<Visual>& visuals) const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
vector<Visual>::const_iterator visual = visuals.begin();
for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
// Set the viewport transform.
ivec2 size = visual->ViewportSize;
ivec2 lowerLeft = visual->LowerLeft;
glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);
// Set the light position.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set the model-view transform.
mat4 rotation = visual->Orientation.ToMatrix();
mat4 modelview = rotation * m_translation;
glLoadMatrixf(modelview.Pointer());
// Set the projection transform.
float h = 4.0f * size.y / size.x;
mat4 projection = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(projection.Pointer());
// Set the diffuse color.
vec3 color = visual->Color * 0.75f;
vec4 diffuse(color.x, color.y, color.z, 1);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse.Pointer());
// Draw the surface.
int stride = sizeof(vec3) + sizeof(vec2);
const GLvoid* texCoordOffset = (const GLvoid*) sizeof(vec3);
const Drawable& drawable = m_drawables[visualIndex];
glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
glVertexPointer(3, GL_FLOAT, stride, 0);
glTexCoordPointer(2, GL_FLOAT, stride, texCoordOffset);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
}
}
void RenderingEngine::SetPngTexture(const string& filename) const
{
//將檔案的information設定給description,然後將圖檔內容放在m_imageData
TextureDescription description = m_resourceManager->LoadImage(filename);
//TextureDescription description = m_resourceManager->GenerateCircle();
GLenum format;
switch (description.Format) {
case TextureFormatGray: format = GL_LUMINANCE; break;
case TextureFormatGrayAlpha: format = GL_LUMINANCE_ALPHA; break;
case TextureFormatRgb: format = GL_RGB; break;
case TextureFormatRgba: format = GL_RGBA; break;
}
GLenum type;
switch (description.BitsPerComponent) {
case 8: type = GL_UNSIGNED_BYTE; break;
case 4:
if (format == GL_RGBA) {
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
}
// intentionally fall through
default:
assert(!"Unsupported format.");
}
void* data = m_resourceManager->GetImageData();
ivec2 size = description.Size;
glTexImage2D(GL_TEXTURE_2D, 0, format, size.x, size.y, 0, format, type, data);
m_resourceManager->UnloadImage();
}
void RenderingEngine::SetPvrTexture(const string& filename) const
{
TextureDescription description = m_resourceManager->LoadPvrImage(filename);
unsigned char* data = (unsigned char*) m_resourceManager->GetImageData();
int width = description.Size.x;
int height = description.Size.y;
int bitsPerPixel;
GLenum format;
bool compressed = true;
switch (description.Format) {
case TextureFormatPvrtcRgba2:
bitsPerPixel = 2;
format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
break;
case TextureFormatPvrtcRgb2:
bitsPerPixel = 2;
format = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
break;
case TextureFormatPvrtcRgba4:
bitsPerPixel = 4;
format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
break;
case TextureFormatPvrtcRgb4:
bitsPerPixel = 4;
format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
break;
default:
compressed = false;
break;
}
if (compressed) {
for (int level = 0; level < description.MipCount; ++level) {
GLsizei size = max(32, width * height * bitsPerPixel / 8);
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, data);
data += size;
width >>= 1; height >>= 1;
}
} else {
GLenum type;
switch (description.Format) {
case TextureFormatRgba:
assert(description.BitsPerComponent == 4);
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
bitsPerPixel = 16;
break;
case TextureFormat565:
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
bitsPerPixel = 16;
break;
case TextureFormat5551:
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_5_5_5_1;
bitsPerPixel = 16;
break;
}
for (int level = 0; level < description.MipCount; ++level) {
glTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, format, type, data);
GLsizei size = width * height * bitsPerPixel / 8;
data += size;
width >>= 1; height >>= 1;
}
}
m_resourceManager->UnloadImage();
}
}
8.RenderingEngine.ES2.cpp
#include <OpenGLES/ES2/gl.h>#include <OpenGLES/ES2/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <assert.h>
#include <iostream>
#include <algorithm>
using namespace std;
namespace ES2 {
#define STRINGIFY(A) #A
#include "./Shaders/TexturedLighting.vert"
#include "./Shaders/TexturedLighting.frag"
struct UniformHandles {
GLuint Modelview;
GLuint Projection;
GLuint NormalMatrix;
GLuint LightPosition;
GLint AmbientMaterial;
GLint SpecularMaterial;
GLint Shininess;
GLint Sampler;
};
struct AttributeHandles {
GLint Position;
GLint Normal;
GLint DiffuseMaterial;
GLint TextureCoord;
};
struct Drawable {
GLuint VertexBuffer;
GLuint IndexBuffer;
int IndexCount;
};
class RenderingEngine : public IRenderingEngine {
public:
RenderingEngine(IResourceManager*);
void Initialize(const vector<ISurface*>& surfaces);
void Render(const vector<Visual>& visuals) const;
void SetPngTexture(const string& name) const;
void SetPvrTexture(const string& file) const;
private:
GLuint BuildShader(const char* source, GLenum shaderType) const;
GLuint BuildProgram(const char* vShader, const char* fShader) const;
vector<Drawable> m_drawables;
GLuint m_colorRenderbuffer;
GLuint m_depthRenderbuffer;
mat4 m_translation;
UniformHandles m_uniforms;
AttributeHandles m_attributes;
IResourceManager* m_resourceManager;
};
IRenderingEngine* CreateRenderingEngine(IResourceManager* resourceManager)
{
return new RenderingEngine(resourceManager);
}
RenderingEngine::RenderingEngine(IResourceManager* resourceManager)
{
m_resourceManager = resourceManager;
glGenRenderbuffers(1, &m_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
}
void RenderingEngine::Initialize(const vector<ISurface*>& surfaces)
{
vector<ISurface*>::const_iterator surface;
for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
// Create the VBO for the vertices.
vector<float> vertices;
unsigned char vertexFlags = VertexFlagsTexCoords;
(*surface)->GenerateVertices(vertices, vertexFlags);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER,
vertices.size() * sizeof(vertices[0]),
&vertices[0],
GL_STATIC_DRAW);
// Create a new VBO for the indices if needed.
int indexCount = (*surface)->GetTriangleIndexCount();
GLuint indexBuffer;
if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
indexBuffer = m_drawables[0].IndexBuffer;
} else {
vector<GLushort> indices(indexCount);
(*surface)->GenerateTriangleIndices(indices);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
indexCount * sizeof(GLushort),
&indices[0],
GL_STATIC_DRAW);
}
Drawable drawable = { vertexBuffer, indexBuffer, indexCount};
m_drawables.push_back(drawable);
}
// Extract width and height.
int width, height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER,
GL_RENDERBUFFER_HEIGHT, &height);
// Create a depth buffer that has the same size as the color buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
// Create the framebuffer object.
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
// Create the GLSL program.
GLuint program = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(program);
// Extract the handles to attributes and uniforms.
m_attributes.Position = glGetAttribLocation(program, "Position");
m_attributes.Normal = glGetAttribLocation(program, "Normal");
m_attributes.DiffuseMaterial = glGetAttribLocation(program, "DiffuseMaterial");
m_attributes.TextureCoord = glGetAttribLocation(program, "TextureCoord");
m_uniforms.Projection = glGetUniformLocation(program, "Projection");
m_uniforms.Modelview = glGetUniformLocation(program, "Modelview");
m_uniforms.NormalMatrix = glGetUniformLocation(program, "NormalMatrix");
m_uniforms.LightPosition = glGetUniformLocation(program, "LightPosition");
m_uniforms.AmbientMaterial = glGetUniformLocation(program, "AmbientMaterial");
m_uniforms.SpecularMaterial = glGetUniformLocation(program, "SpecularMaterial");
m_uniforms.Shininess = glGetUniformLocation(program, "Shininess");
m_uniforms.Sampler = glGetUniformLocation(program, "Sampler");
// Set the active sampler to stage 0. Not really necessary since the uniform
// defaults to zero anyway, but good practice.
glActiveTexture(GL_TEXTURE0);
glUniform1i(m_uniforms.Sampler, 0);
// Set up the texture state.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Set up some default material parameters.
glUniform3f(m_uniforms.AmbientMaterial, 0.04f, 0.04f, 0.04f);
glUniform3f(m_uniforms.SpecularMaterial, 0.5, 0.5, 0.5);
glUniform1f(m_uniforms.Shininess, 50);
// Initialize various state.
glEnableVertexAttribArray(m_attributes.Position);
glEnableVertexAttribArray(m_attributes.TextureCoord);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Set up transforms.
m_translation = mat4::Translate(0, 0, -7);
}
void RenderingEngine::Render(const vector<Visual>& visuals) const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
vector<Visual>::const_iterator visual = visuals.begin();
for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
// Set the viewport transform.
ivec2 size = visual->ViewportSize;
ivec2 lowerLeft = visual->LowerLeft;
glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);
// Set the model-view transform.
mat4 modelview = m_translation;
glUniformMatrix4fv(m_uniforms.Modelview, 1, 0, modelview.Pointer());
// Set the projection transform.
float h = 4.0f * size.y / size.x;
// 參數的調整方向暫時還無法瞭解,使用試誤法找出最佳值,只要near值大於7.x就無法顯示影像,此處在調整顯示的位置與大小
//mat4 projectionMatrix = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10); // original
mat4 projectionMatrix = mat4::Frustum(-1.5f, 0.8f, -1, 1, 7.0f, 10);
glUniformMatrix4fv(m_uniforms.Projection, 1, 0, projectionMatrix.Pointer());
// Set the diffuse color.
vec3 color = visual->Color * 0.75f;
glVertexAttrib4f(m_attributes.DiffuseMaterial, color.x, color.y, color.z, 1);
// Draw the surface.
int stride = sizeof(vec3) + sizeof(vec2);
const GLvoid* texCoordOffset = (const GLvoid*) sizeof(vec3);
GLint position = m_attributes.Position;
GLint texCoord = m_attributes.TextureCoord;
const Drawable& drawable = m_drawables[visualIndex];
glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);
glVertexAttribPointer(texCoord, 2, GL_FLOAT, GL_FALSE, stride, texCoordOffset);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
}
}
GLuint RenderingEngine::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]);
cout << messages;
exit(1);
}
return shaderHandle;
}
GLuint RenderingEngine::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]);
cout << messages;
exit(1);
}
return programHandle;
}
void RenderingEngine::SetPngTexture(const string& filename) const
{
//將檔案的information設定給description,然後將圖檔內容放在m_imageData
TextureDescription description = m_resourceManager->LoadImage(filename);
//TextureDescription description = m_resourceManager->GenerateCircle();
GLenum format;
switch (description.Format) {
case TextureFormatGray: format = GL_LUMINANCE; break;
case TextureFormatGrayAlpha: format = GL_LUMINANCE_ALPHA; break;
case TextureFormatRgb: format = GL_RGB; break;
case TextureFormatRgba: format = GL_RGBA; break;
}
GLenum type;
switch (description.BitsPerComponent) {
case 8: type = GL_UNSIGNED_BYTE; break;
/*case 4: // PNG檔沒有4bits的,所以可移除
if (format == GL_RGBA) {
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
}
// intentionally fall through */
default:
assert(!"Unsupported format.");
}
void* data = m_resourceManager->GetImageData();
ivec2 size = description.Size;
glTexImage2D(GL_TEXTURE_2D, 0, format, size.x, size.y, 0, format, type, data);
m_resourceManager->UnloadImage();
}
void RenderingEngine::SetPvrTexture(const string& filename) const
{
TextureDescription description = m_resourceManager->LoadPvrImage(filename);
unsigned char* data = (unsigned char*) m_resourceManager->GetImageData();
int width = description.Size.x;
int height = description.Size.y;
int bitsPerPixel;
GLenum format;
bool compressed = true;
switch (description.Format) {
case TextureFormatPvrtcRgba2:
bitsPerPixel = 2;
format = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
break;
case TextureFormatPvrtcRgb2:
bitsPerPixel = 2;
format = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
break;
case TextureFormatPvrtcRgba4:
bitsPerPixel = 4;
format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
break;
case TextureFormatPvrtcRgb4:
bitsPerPixel = 4;
format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
break;
default:
compressed = false; // 如果不是以上格式,就是非壓縮模式
break;
}
if (compressed) { // 對每一個 Mipmap資料都去讀取進來
for (int level = 0; level < description.MipCount; ++level) {
GLsizei size = max(32, width * height * bitsPerPixel / 8);
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, data);
data += size; // 將pointer指向下一個Mipmap的位置
width >>= 1; height >>= 1; // 每一個Mipmap都是前一個的一半大小
}
} else {
GLenum type;
switch (description.Format) {
case TextureFormatRgba:
assert(description.BitsPerComponent == 4);
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
bitsPerPixel = 16;
break;
case TextureFormat565:
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
bitsPerPixel = 16;
break;
case TextureFormat5551:
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_5_5_5_1;
bitsPerPixel = 16;
break;
}
for (int level = 0; level < description.MipCount; ++level) {
GLsizei size = width * height * bitsPerPixel / 8;
glTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, format, type, data);
data += size;
width >>= 1; height >>= 1;
}
}
m_resourceManager->UnloadImage();
}
}
9. ResourceManager.mm,處理載入檔案的細節程式碼
#import <UIKit/UIKit.h>#import <QuartzCore/QuartzCore.h>
#import <string>
#import <iostream>
#import "Interfaces.hpp"
#import "./PowerVR/PVRTTexture.h"
using namespace std;
class ResourceManager : public IResourceManager {
public:
ResourceManager()
{
m_imageData = 0;
}
string GetResourcePath() const
{
NSString* bundlePath =[[NSBundle mainBundle] resourcePath];
return [bundlePath UTF8String];
}
TextureDescription LoadImage(const string& file)
{
NSString* basePath = [NSString stringWithUTF8String:file.c_str()];
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
NSString* resourcePath2 = [resourcePath stringByAppendingString:@"/Textures"]; // add by kk
NSString* fullPath = [resourcePath2 stringByAppendingPathComponent:basePath];
UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath];
TextureDescription description;
description.Size.x = CGImageGetWidth(uiImage.CGImage);
description.Size.y = CGImageGetHeight(uiImage.CGImage);
description.BitsPerComponent = 8; // 此處所讀檔案一定是8bits模式, normal picture file
description.Format = TextureFormatRgba;
description.MipCount = 1;
m_hasPvrHeader = false;
// one component = R, G, B, A的任一個, bpp = bits per pixel , so normal png = RGBA 32bits = 32bpp
int bpp = description.BitsPerComponent / 2; // RGBA = 24bits = 4bytes = 8(bits)/2
int byteCount = description.Size.x * description.Size.y * bpp;// 這裡的bpp 是bytes per pixel
unsigned char* data = (unsigned char*) calloc(byteCount, 1);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
// 當data這個記憶區塊定址好了,使用CGBitmapContextCreate這個function,將其context轉換制定成一個圖形儲存空間
CGContextRef context = CGBitmapContextCreate(data,
description.Size.x,
description.Size.y,
description.BitsPerComponent,
bpp * description.Size.x,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
CGRect rect = CGRectMake(0, 0, description.Size.x, description.Size.y);
CGContextDrawImage(context, rect, uiImage.CGImage);
CGContextRelease(context);
m_imageData = [NSData dataWithBytesNoCopy:data length:byteCount freeWhenDone:YES];
//使用方法 http://stackoverflow.com/questions/8691997/behavior-of-nsdata-initwithbytesnocopylengthfreewhendone
return description;
}
TextureDescription GenerateCircle() // 在OPENGLES上面畫上一個圓圈
{
TextureDescription description;
description.Size = ivec2(256, 256);
description.BitsPerComponent = 8;
description.Format = TextureFormatRgba;
int bpp = description.BitsPerComponent / 2;
int byteCount = description.Size.x * description.Size.y * bpp;
unsigned char* data = (unsigned char*) calloc(byteCount, 1);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
CGContextRef context = CGBitmapContextCreate(data,
description.Size.x,
description.Size.y,
description.BitsPerComponent,
bpp * description.Size.x,
colorSpace,
bitmapInfo);
CGColorSpaceRelease(colorSpace);
CGRect rect = CGRectMake(5, 5, 246, 246);
CGContextSetRGBFillColor(context, 0, 0, 1, 1);
CGContextFillEllipseInRect(context, rect);
CGContextRelease(context);
m_imageData = [NSData dataWithBytesNoCopy:data length:byteCount freeWhenDone:YES];
return description;
}
TextureDescription LoadPvrImage(const string& file)
{
NSString* basePath = [NSString stringWithUTF8String:file.c_str()];
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
NSString* resourcePath2 = [resourcePath stringByAppendingString:@"/Textures"]; // 圖檔的次目錄
NSString* fullPath = [resourcePath2 stringByAppendingPathComponent:basePath];
m_imageData = [NSData dataWithContentsOfFile:fullPath];
m_hasPvrHeader = true;
PVR_Texture_Header* header = (PVR_Texture_Header*) [m_imageData bytes];
bool hasAlpha = header->dwAlphaBitMask ? true : false;
TextureDescription description;
//低精度的非壓縮格式(565,5551,4444)往往被忽視。不同於塊壓縮,它們不會導致斑點影像中的工件。雖然他們不能很好地工作,他們是相當不錯的圖像,平滑的色彩梯度保存在照片中的細節,並保持乾淨的線條簡單的矢量藝術。
switch (header->dwpfFlags & PVRTEX_PIXELTYPE) {
case OGL_RGB_565: //
description.Format = TextureFormat565;
break;
case OGL_RGBA_5551:
description.Format = TextureFormat5551;
break;
case OGL_RGBA_4444:
description.Format = TextureFormatRgba;
description.BitsPerComponent = 4;
break;
case OGL_PVRTC2:
description.Format = hasAlpha ? TextureFormatPvrtcRgba2 :
TextureFormatPvrtcRgb2;
break;
case OGL_PVRTC4:
description.Format = hasAlpha ? TextureFormatPvrtcRgba4 :
TextureFormatPvrtcRgb4;
break;
default:
assert(!"Unsupported PVR image.");
break;
}
description.Size.x = header->dwWidth;
description.Size.y = header->dwHeight;
description.MipCount = header->dwMipMapCount;
if (description.MipCount ==0) // 當Mipmap count 為零時,強制設為一
description.MipCount =1; // 目前還不知道0與1的差別。
return description;
}
void* GetImageData()
{
if (!m_hasPvrHeader)
return (void*) [m_imageData bytes];
PVR_Texture_Header* header = (PVR_Texture_Header*) [m_imageData bytes];
char* data = (char*) [m_imageData bytes];
unsigned int headerSize = header->dwHeaderSize;
return data + headerSize;
}
void UnloadImage()
{
m_imageData = 0;
}
private:
NSData* m_imageData;
bool m_hasPvrHeader;
};
IResourceManager* CreateResourceManager()
{
return new ResourceManager();
}




沒有留言:
張貼留言