顯示具有 OPENGLES 標籤的文章。 顯示所有文章
顯示具有 OPENGLES 標籤的文章。 顯示所有文章

2013年7月17日 星期三

OpenGL基本實作(十五)(PVRTC 載入)


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();
}


10. run script的部分,使用apple的texturetool,及時產生PVRTC檔,雖然不需要及時,只是展示了可及時Encode的功能罷了。






11. 結果顯示,只是一個平面,用來顯示texture檔的讀取並顯示出來。可以自動輪動。




2013年7月15日 星期一

選擇一個 open source engine for game

選擇一個適合且免費的遊戲引擎,目前的首選

2D 使用
Cocos2D – Framework for building 2D games for iPod Touch, iPhone and iPad. Claims to be used by more than 2500 games on the App Store.

Sparrow Framework
新發現的game engine,只有2D功能。目前看來還蠻多游戲用它來開發,表列在此

3D 使用
Oolong – Free to use game engine written in C++ that lets you create new iOS games and port existing games to iOS devices. 更新只到 2012年,原因可能是開發者已經到業界上班了。

開放原始碼的遊戲引擎,使用Bullet物理引擎,但文件較不完整。遊戲開發初學者不建議使用。


Irrlicht Engine一個開源的跨平台3D engine,資料可參考wiki,似乎很強大,但好像沒有看到iphone上有誰使用它來開發。


Unreal Development Kit – Free version of the industry-leading Unreal Engine III. UDK is used to create games, apps and advanced 3D simulations. Supports iOS and Android. 沒有OPEN source

NME – Free open source framework that lets you develop Android, iOS, BlackBerry and Windows Phone apps from a single codebase. No C or C++ skills required.

3D engine難選,怕是花了時間學了,最後卻要收費並且也未必好用。Unity最多人推薦,但收費也最貴,暫時先直接使用OPENGLES,寫一些基本就好了。


參考網址
http://maniacdev.com/2009/08/the-open-source-iphone-game-engine-comparison

http://www.mobyaffiliates.com/blog/ios-android-mobile-game-development-tools-frameworks-engines-resources/

http://stackoverflow.com/questions/12068018/choosing-3d-engine-for-ios-in-c

2013年7月8日 星期一

Imagination公司的PVRTC


iPhone的圖形芯片(PowerVR MBX)對一種稱為 PVRTC 的壓縮技術提供硬件支持,Apple推薦在開發iPhone應用程序時使用 PVRTC 紋理。他們甚至提供了一篇很好的 技術筆記 描述了怎樣通過使用隨開發工具安裝的命令行程序將標準圖像文件轉換為 PVRTC 紋理的方法。
你應該知道當使用 PVRTC 時與標準JPEG或PNG圖像相比有可能有些圖像質量的下降。是否值得在你的程序中做出一些犧牲取決於一些因素,但使用 PVRTC 紋理可以節省大量的內存空間。
儘管因為沒有Objective-C類可以解析 PVRTC 數據獲取其寬和高1信息,你想要手工指定圖像的高和寬,但加載 PVRTC 數據到當前綁定的紋理實際上甚至比加載普通圖像文件更為簡單。
 
下面的例子使用默認的texturetool設置加載一個PVRTC紋理:
    NSString *path = [[NSBundle mainBundle] pathForResource:@"texture" ofType:@"pvrtc"];
    NSData *texData = [[NSData alloc] initWithContentsOfFile:path];
    glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 512, 512, 0,
        [texData length], [texData bytes]);
 
使用glCompressedTexImage2D()從文件加載數據並傳送給OpeNGL ES。而隨後怎樣處理紋理則絕對沒有任何區別。

glCompressedTexImage2D定義了二維紋理圖像或者立方體映射紋理圖像,圖像數據是壓縮的並存儲在客戶端內存中。紋理圖形根據internalformat指定的格式來解碼。 OpenGL ES並沒有指定壓縮紋理格式,但是它提供一個機制:獲取這些由擴展名指定的格式所對應的OpenGL符號​​常量。所支持壓縮紋理格式數量可以查詢GL_NUM_COMPRESSED_TEXTURE_FORMATS值來獲取。
所支持的壓縮格式列表可以查詢GL_COMPRESSED_TEXTURE_FORMATS值來獲取。 
 
使用參考 http://www.dreamingwish.com/dream-2012/glcompressedteximage2d.html 
 
 
 
 
而壓縮PVRTC可使用 Apple提供一個內建的壓縮工具texturetool,
這是一個shell程式,因此可以內嵌在xcode伴隨及時執行
位置在/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
 

操作方法及範例
texturetool -m -e PVRTC -f PVR -p Preview.png -o Grid16.pvr Grid16.png 

Some of the parameters are explained below.

-m
Generate mipmaps.
-e PVRTC
Use PVRTC compression. This can be tweaked with additional parameters, explained below.
-f PVR
This may seem redundant, but it chooses the file format rather than the encoding. The PVR format includes a simple header before the image data that contains size and format information. I'll explain how to parse the header later.
-p PreviewFile
This is an optional PNG file that gets generated to allow you preview the quality loss caused by compression.
-o OutFile
The name of the resulting PVR file.
The encoding argument can be tweaked with optional arguments. Some examples:
只支援下面兩種模式,其他的RGBA5551 RGBA8888  RGBA4444 ....都不支援。
-e PVRTC --bits-per-pixel-2
Specifies a 2 bits-per-pixel encoding.
-e PVRTC --bits-per-pixel-4
Specifies a 4 bits-per-pixel encoding. This is the default, so there's not much reason to include it on the command line.
-e PVRTC --channel-weighting-perceptual -bits-per-pixel-2
Use perceptual compression and a 2 bpp format. Perceptual compression doesn't change the format of the image data; rather, it tweaks the compression algorithm such that the green channel preserves more quality than the red and blue channels. Humans are more sensitive to variations in green.
-e PVRTC --channel-weighting-linear
Apply compression equally to all color components. This defaults to "on", so there's no need to specify it explicitly.
注意: Apple tool 不產生Mipmap的部位。



一般使用的壓縮檔都是事先轉換好的,使用Script來同步執行有些麻煩,可以使用Imagination提供的SDK來做,這是GUI的Tool。並且所有的壓縮模式都支援。

工具下载地址
http://www.imgtec.com/powervr/insider/sdkdownloads/index.asp?installer=Windows%20Installer



安裝後的參考目錄



由於需要其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


Version2與Version3的Header並不相同,Imagination的Tool預設市Version3,使用Save As Legacy才能存成version2,參考如圖



Version 2 PVR Texture Header
typedef struct _PVRTexHeader
{
    uint32_t headerLength;
    uint32_t height;
    uint32_t width;
    uint32_t numMipmaps;
    uint32_t flags;
    uint32_t dataLength;
    uint32_t bpp;
    uint32_t bitmaskRed;
    uint32_t bitmaskGreen;
    uint32_t bitmaskBlue;
    uint32_t bitmaskAlpha;
    uint32_t pvrTag;
    uint32_t numSurfs;
} PVRTexHeader;
 
Version 3 PVR Texture Header
typedef struct _PVRTexHeaderV3{
    uint32_t    version;            
    uint32_t    flags;          
    uint64_t    pixelFormat;        
    uint32_t    colourSpace;        
    uint32_t    channelType;        
    uint32_t    height;         
    uint32_t    width;          
    uint32_t    depth;          
    uint32_t    numSurfaces;        
    uint32_t    numFaces;       
    uint32_t    numMipmaps;     
    uint32_t    metaDataSize;   
} PVRTexHeaderV3;




最後texture的壓縮格式,因為格式太多,似乎有些難懂。有需要的時候再來仔細研究
在這之前,先用Apple所提供的Tool來實作吧

官方資料   http://www.opengl.org/wiki/Image_Format

一個壓縮texture的Paper
http://titania-x3d.googlecode.com/git/Papers/ARB_texture_compression.pdf

壓縮格式的代號,參考nvidia
http://developer.download.nvidia.com/opengl/includes/glext.h

常见的压缩纹理格式
基于OpenGL ES的压缩纹理有常见的如下几种实现:
1
ETC1(Ericsson texture compression)
2
PVRTC (PowerVR texture compression)
3
ATITC (ATI texture compression)
4
S3TC (S3 texture compression)

ETC1:
ETC1格式是OpenGL ES图形标准的一部分,并且被所有的Android设备所支持。
扩展名为: GL_OES_compressed_ETC1_RGB8_texture,不支持透明通道,所以仅能用于不透明纹理。
当加载压缩纹理时,<internal format>参数支持如下格式:
    GL_ETC1_RGB8_OES(RGB,每个像素0.5个字节)

PVRTC:
支持的GPU为Imagination Technologies的PowerVR SGX系列。
OpenGL ES的扩展名为: GL_IMG_texture_compression_pvrtc。
当加载压缩纹理时,<internal format>参数支持如下几种格式:
    GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG (RGB,每个像素0.5个字节)
    GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG (RGB,每个像素0.25个字节)
    GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG (RGBA,每个像素0.5个字节)
    GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG (RGBA,每个像素0.25个字节)










ATITC:
支持的GPU为Qualcomm的Adreno系列。
支持的OpenGL ES扩展名为: GL_ATI_texture_compression_atitc。
当加载压缩纹理时,<internal format>参数支持如下类型的纹理:
    GL_ATC_RGB_AMD (RGB,每个像素0.5个字节)
    GL_ATC_RGBA_EXPLICIT_ALPHA_AMD (RGBA,每个像素1个字节)
    GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD (RGBA,每个像素1个字节)

S3TC
也被称为DXTC,在PC上广泛被使用,但是在移动设备上还是属于新鲜事物。支持的GPU为NVIDIA Tegra系列。
OpenGL ES扩展名为:
GL_EXT_texture_compression_dxt1和GL_EXT_texture_compression_s3tc。
当加载压缩纹理时,<internal format>的参数有如下几种格式:
    GL_COMPRESSED_RGB_S3TC_DXT1 (RGB,每个像素0.5个字节)
    GL_COMPRESSED_RGBA_S3TC_DXT1 (RGBA,每个像素0.5个字节)
    GL_COMPRESSED_RGBA_S3TC_DXT3 (RGBA,每个像素1个字节)
    GL_COMPRESSED_RGBA_S3TC_DXT5 (RGBA,每个像素1个字节)

    由此可见,Mali系列GPU只支持ETC1格式的压缩纹理,而且该纹理不支持透明通道,有一定局限性。
    以上压缩纹理格式每个像素大小相对A8R8G8B8格式的比例,最高压缩比是16:1,最低压缩比是4:1,对于减小纹理的数据容量有明显作用,相应在显 存带宽上也有明显优势,从而提高游戏的运行效率(此特性没有绝对数值,根据每个游戏的用法和瓶颈点不同而有差别)。



在使用glCompressedTexImage2D來讀取資料時,需要將壓縮格式告知,放在format這個變數上
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, data);

壓縮代碼定義在<OpenGLES/ES2/glext.h>

/* GL_IMG_texture_compression_pvrtc */
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG                      0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG                      0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG                     0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG                     0x8C03
#endif

/* GL_IMG_texture_compression_pvrtc2 */
#ifndef GL_IMG_texture_compression_pvrtc2
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG                     0x9137
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG                     0x9138
#endif



參考網址

http://m.mydrivers.com/newsview.aspx?id=266555&cid=1&p=4

http://zh.wikipedia.org/wiki/PowerVR

https://developer.apple.com/library/ios/#qa/qa2008/qa1611.html

http://www.tuicool.com/articles/meuiii

http://wiki.sparrow-framework.org/manual/pvr_textures