2013年6月15日 星期六

OpenGL基本瞭解(十六) (Polygonal modeling)

多邊形造型(Polygonal modeling)


參考wiki的說明  http://zh.wikipedia.org/wiki/%E5%A4%9A%E8%BE%B9%E5%BD%A2%E9%80%A0%E5%9E%8B

三維電腦圖形學中,多邊形造型是用多邊形表示或者近似表示物體曲面的物體造型方法。多邊形造型非常適合於掃描線渲染,因此即時電腦圖形處理中的一項可以使用的方法。其它表示三維物體的方法有 NURBS 曲面、細分曲面以及光線跟蹤中所用的基於方程的表示方法。




以下是一個使用OPENGL1.1的範例參考網頁

在使用OpenGL繪製3D模型時
可以用glPolygonMode()搭配參數GL_LINE檢視模型網格的架構
不過,如果要「同時」顯示模型實體(solid)跟網格線(wireframe),該怎麼做好呢?

答案出乎意料的簡單,OpenGL Redbook上就有說明
利用glPolygonOffset()加上glPolygonMode()
將模型畫兩次,就可以達成囉~
為了減少程式碼的複雜度,建議先將繪圖部份以display list表示
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glShadeModel(GL_FLAT);
glColor3f( model_color );
glCallList( model_display_list );

glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f( wireframe_color );
glCallList( model_display_list );

如此一來,就可以畫出表面有網格線的模型實體了


苹果在iOS 7中弃用了哪些API

我们知道苹果在iOS 7 SDK中开放了超过1500个新的API,但我们也知道随着开发技术和设备的进步,有些过时的API是必须废弃掉的,我们来看看苹果在iOS 7中弃用了哪些API。

轉貼自  http://www.cocoachina.com/applenews/devnews/2013/0614/6411.html


1.The Map Kit framework includes deprecations for the MKOverlayView class and its various subclasses. The existing overlay views have been replaced with an updated set of overlay renderer objects that descend from the MKOverlayRenderer class. For more information about the classes of this framework, see Map Kit Framework Reference.
2.The AudioSession API in the Audio Toolbox framework is deprecated. Applications should use theAVAudioSession  class in the AV Foundation framework instead.
3.The CLRegion class in the Core Location framework is replaced by the CLCircularRegion  class. The CLRegion class continues to exist as an abstract base class that supports both geographic and beacon regions.
4.The UUID property of the CBCentral class is deprecated. To specify the unique ID of your central objects, use the identifier property instead.
5.The Game Kit framework contains assorted deprecations intended to clean up the existing API and provide better support for new features.
6.The UIKit framework contains the following deprecations:
The wantsFullScreenLayout property of UIViewController is deprecated. In iOS 7 and later, view controllers always support full screen layout.
The  UIPopoverController  class no longer supports the notion of an arrow direction; it supports a presentation direction.
UIColor  objects that provided background textures for earlier versions of iOS are gone.
7.Many drawing additions to the NSString class are deprecated in favor of newer variants.
8.The gethostuuid function in the NSString  library is deprecated.
9.In iOS 7 and later, if you ask for the MAC address of an iOS device, the system returns the value 02:00:00:00:00:00. If you need to identify the device, use the identifierForVendor  property of UIDevice instead. (Apps that need an identifier for their own advertising purposes should consider using the advertisingIdentifier  property of ASIdentifierManager instead.)
不过,苹果也不会立刻终结某些API的寿命,会有一个限定时期以方便开发者过渡至新的更好的API。有时,一些API可能是在某些事件暂停使用,做一些更改,而有的API可能会永远远离操作系统了。
作为一个开发者,要尽量避免使用已经弃用的API,至少新代码或者新项目中不能再使用弃用的API,如果现在的代码中使用了弃用的API,要尽可能快地更新你的代码。不过,编译器会在使用弃用API的时候给予提醒。

更多API变更的详细信息可查看:iOS 7.0 API Diffs.

OpenGL基本瞭解(十五) (法線)

法線Normal (geometry)

是垂直於表面的一條線


p = Evaluate(s, t)   (黑點位置)
u = Evaluate(s + ds, t) - p     (ds 極小值,相對於P以得到u線)
v = Evaluate(s, t + dt) - p      (dt 極小值,相對於P以得到v線)
N = Normalize(u × v)   (N = u , v乘積)

C++的乘積範例,此做法是由ㄧ個點,使用微小差異的方式來得出兩個向量,以此向量得出的平面,來求出法向量。
template <typename T>
struct Vector3 {
    // ...
    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 x, y, z;
};
 
 
 
 
在一般的3D資料中,如果給定的是face面資料,可以根據三角形之類或其它面型的點位置,
利用三點坐標去算出法向量
 
例如 
for each face in faces:
    vec3 a = positions[face.Vertex0]
    vec3 b = positions[face.Vertex1]
    vec3 c = positions[face.Vertex2]
    vec3 facetNormal = (a - b) × (c - b) 
 

再將facetNormal加到每一個點上,但因為每一個點可能對應到不同的面上,
然後我們將一個點上所對應到的多個法向量相加,就可以得多此點所對得的真實向量了。
 
 
 

OpenGL基本實作(十一) (Per-Pixel Lighting & Toon Shading)

延續前一個實作練習原本的是vertex lighting,加入Per-Pixel LightingToon Shading,所以加入一個segmented Controller來做選擇器,來比對三者之間的效果。

在計算機圖形學中Per-Pixel Lighting是指照明的圖像或場景的渲染圖像上的每個像素,計算照明的任何技術。這是對比到其他流行的照明方法,如vertex lighting,其計算照明的3D模型的每一個頂點,然後內插模型的多面得到的值,計算最終的每個像素的顏色值。

Per-Pixel Lighting是常用技術,如法線貼圖,凹凸貼圖,鏡面反射,陰影卷。這些技術中的每一個都被點亮的表面提供了一些附加的數據或場景,和光源的最終外觀和感覺的表面。

大多數現代視頻遊戲引擎實現照明採用
Per-Pixel Lighting的技術,而不是vertex lighting,以實現增加細節和真實感。 如Doom3等遊戲的,其核心引擎是實現一個完全的Per-Pixel Lighting著色引擎的第一場遊戲




Toon Shading可參考wiki網址上的說明,與常規渲染不同的是,卡通渲染的光照效果是經過去真實感處理的。最常見的就是NPR效果。參考下面網頁可以比對其中的效果。

參考網頁http://tw.myblog.yahoo.com/teddy-animtion/article?mid=1098



參考3D Programming書上所提供的技術,主要修改GLSL檔的內容,其他部分並無需修改。但是需在前一個實作中加入Segmented Controller,因此些微修改控制的部分,惟一的缺點是,每一次切換會讓繪圖物件的方位回到初始位置。

Per-Pixel Lighting

PixelLighting.es2.frag檔的內容
static const char* SimpleFragmentShader4Pixel = STRINGIFY(

varying mediump vec3 EyespaceNormal;
varying lowp vec3 Diffuse;

uniform highp vec3 LightPosition;
uniform highp vec3 AmbientMaterial;
uniform highp vec3 SpecularMaterial;
uniform highp float Shininess;

void main(void)
{

//EyespaceNormal = NormalMatrix * Normal;
highp vec3 N = normalize(EyespaceNormal);  //與vertex lighting差在 normalize
highp vec3 L = normalize(LightPosition);
highp vec3 E = vec3(0, 0, 1);     // 當觀眾在無限遠處時,E可以簡化成  [0, 0, 1]
highp vec3 H = normalize(L + E);

highp float df = max(0.0, dot(N, L));
highp float sf = max(0.0, dot(N, H));
sf = pow(sf, Shininess);

// Diffuse = DiffuseMaterial;
lowp vec3 color = AmbientMaterial + df * Diffuse + sf * SpecularMaterial;

gl_FragColor = vec4(color, 1);  // gl_FragColor = DestinationColor;
}
                                                        
);



PixelLighting.es2.vert檔的內容
static const char* SimpleVertexShader4Pixel = STRINGIFY(

attribute vec4 Position;
attribute vec3 Normal;
attribute vec3 DiffuseMaterial;

uniform mat4 Projection;
uniform mat4 Modelview;
uniform mat3 NormalMatrix;

varying vec3 EyespaceNormal;
varying vec3 Diffuse;

void main(void)
{
EyespaceNormal = NormalMatrix * Normal;
Diffuse = DiffuseMaterial;
gl_Position = Projection * Modelview * Position;
}
);

vertex lighting相比,只是將原來Simple.es2.vert檔運算的部分轉移到PixelLighting.es2.frag,並將N計算多一個Normalize而已,其它看起來都一樣。

Toon Shading

vertex部分與PixelLighting.es2.vert相同,因此延用。以下是Fragment部分

ToonShading.es2.frag

static const char* ToonShader = STRINGIFY(

varying mediump vec3 EyespaceNormal;
varying lowp vec3 Diffuse;

uniform highp vec3 LightPosition;
uniform highp vec3 AmbientMaterial;
uniform highp vec3 SpecularMaterial;
uniform highp float Shininess;

void main(void)
{
highp vec3 N = normalize(EyespaceNormal);
highp vec3 L = normalize(LightPosition);
highp vec3 E = vec3(0, 0, 1);
highp vec3 H = normalize(L + E);

highp float df = max(0.0, dot(N, L));
highp float sf = max(0.0, dot(N, H));
sf = pow(sf, Shininess);

// 卡通效果,與PixelLighting不同之處
//--------------------------------------
if (df < 0.1) df = 0.0;
else if (df < 0.3) df = 0.3;
else if (df < 0.6) df = 0.6;
else df = 1.0;

sf = step(0.5, sf);
//---------------------------------------


lowp vec3 color = AmbientMaterial + df * Diffuse + sf * SpecularMaterial;

gl_FragColor = vec4(color, 1);
}
);


為了將以上兩種效果與vertex lighting作比較,因此在程式中加入Segmented Controller,以下就差異的部分列出。

1. 檔案列表,多出三個GLSL檔



 

2. mainViewController.mm


#import "mainViewController.h"

@interface mainViewController ()
{
    int GLSL_mode;
}

@end

@implementation mainViewController
{

    UISlider  *sIconXRotateSlider;
    UISlider  *sIconYRotateSlider;
    UISlider  *sIconZRotateSlider;
   
    UISlider  *lightPosXSlider;
    UISlider  *lightPosYSlider;
    UISlider  *lightPosZSlider;
   
    UISegmentedControl *GLSL_Selector;
  
 
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
   
    m_window = [[UIWindow alloc] initWithFrame: screenBounds];
   
    m_view = [[GLView alloc] initWithFrame: screenBounds];

    [m_window addSubview: m_view];
    [m_window makeKeyAndVisible];
   
    [self setSlideInterface1];
    [self setSlideInterface2];
    [self setSlideInterface3];
   
    [self setSlideInterfaceLightX];
    [self setSlideInterfaceLightY];
    [self setSlideInterfaceLightZ];
   
    [self setGLSL_Selector];
   

    [m_window addSubview:sIconXRotateSlider];
    [m_window addSubview:sIconYRotateSlider];
    [m_window addSubview:sIconZRotateSlider];
   
    [m_window addSubview:lightPosXSlider];
    [m_window addSubview:lightPosYSlider];
    [m_window addSubview:lightPosZSlider];
   
    [m_window addSubview:GLSL_Selector];
   
    GLSL_mode = 0;

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(BOOL)shouldAutorotate{
    return YES;
}

// 轉到Landscape 模式
-(NSInteger)supportedInterfaceOrientations{
   
    //    UIInterfaceOrientationMaskLandscape;
    //    24
    //
    //    UIInterfaceOrientationMaskLandscapeLeft;
    //    16
    //
    //    UIInterfaceOrientationMaskLandscapeRight;
    //    8
    //
    //    UIInterfaceOrientationMaskPortrait;
    //    2
   
    //    return UIInterfaceOrientationMaskPortrait;
    //    or
    return 24;
}

.....

- (void) setGLSL_Selector
{
    //[self removeSubViewByLabelClass];
   
    NSArray *itemArray =[NSArray arrayWithObjects:@"vertex lighting", @"pixel lighting", @"Toon Shading",nil];
   
    //使用陣列來建立UISegmentedControl
    GLSL_Selector = [[UISegmentedControl alloc] initWithItems:itemArray];
   
    //設定外觀大小與初始選項
    GLSL_Selector.segmentedControlStyle = UISegmentedControlStyleBar;
    GLSL_Selector.frame = CGRectMake(20.0, 100.0, 500.0, 44.0);
   
    [GLSL_Selector setCenter:CGPointMake(700, 500)];
   
    GLSL_Selector.selectedSegmentIndex = 0;     
    GLSL_Selector.tag = 1;
   
    // 由於設定成LandScape,X在垂直方向
    GLSL_Selector.transform = CGAffineTransformMakeRotation( M_PI / 2.0);
   
    //設定所觸發的事件條件與對應事件
    [GLSL_Selector addTarget:self action:@selector(chooseOne:) forControlEvents:UIControlEventValueChanged];
   
    //加入畫面中並釋放記憶體
    [self.view addSubview:GLSL_Selector];
   
}

- (void)chooseOne:(id)sender {
   
    GLSL_mode = [sender selectedSegmentIndex];
   
    [m_view setGLSL:GLSL_mode];
   
    [m_view initSet:m_window.frame];
   
}

@end




3. GLView.h

#import <UIKit/UIKit.h>

#import "Interfaces.hpp"
#import <QuartzCore/QuartzCore.h>

@interface GLView : UIView
{
@private
    IApplicationEngine* m_applicationEngine;
    IRenderingEngine* m_renderingEngine;
    EAGLContext* m_context;
    float m_timestamp;
   
@public
    float smallIconRotateXValue;
    float smallIconRotateYValue;
    float smallIconRotateZValue;
   
    float  lightPosX;
    float  lightPosY;
    float  lightPosZ;
   
    int GLSL_mode;
   
    Quaternion old_orientation;
   
    CADisplayLink* displayLink;
}

- (void) drawView: (CADisplayLink*) displayLink;

- (id) initSet:(CGRect) frame;

- (void) setGLSL:(int) mode;

- (void) resetdisplayLink;

@end




4. GLView.mm

#import "GLView.h"

@implementation GLView
{

}

+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

- (id) initWithFrame: (CGRect) frame
{
    smallIconRotateXValue =0;
    smallIconRotateYValue =0;
    smallIconRotateZValue =0;
   
    lightPosX =0.25;
    lightPosY =0.25;
    lightPosZ =1;
   
    GLSL_mode = 0;
   

    old_orientation = Quaternion(0, 0, 0, 1);
   
    if (self = [super initWithFrame:frame])
    {
        if  ([self initSet:frame] == nil)
            return nil;
    }
    return self;
}

- (id) initSet:(CGRect) frame
{
    CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
    eaglLayer.opaque = YES;

    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    //EAGLRenderingAPI 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 = WireframeES1::CreateRenderingEngine();
        m_renderingEngine = SolidES1::CreateRenderingEngine();
    } else {
        NSLog(@"Using OpenGL ES 2.0");
        //m_renderingEngine = WireframeES2::CreateRenderingEngine(); // 完成 m_colorRenderbuffer 設定
       
        m_renderingEngine = SolidES2::CreateRenderingEngine();  // replace
    }
   
    m_applicationEngine = ParametricViewer::CreateApplicationEngine(m_renderingEngine);
   
    m_applicationEngine->SetIconRotateValue(smallIconRotateXValue, smallIconRotateYValue, smallIconRotateZValue);
    

  m_applicationEngine->setGLSL(GLSL_mode);
  
 
    [m_context
     renderbufferStorage:GL_RENDERBUFFER
     fromDrawable: eaglLayer];
   
    int width = CGRectGetWidth(frame);
    int height = CGRectGetHeight(frame);
    m_applicationEngine->Initialize(width, height);
   
    [self setdisplayLink];
   
    return self;
}

- (void) setGLSL:(int)mode
{
    GLSL_mode = mode;
}


- (void) setdisplayLink
{
    [self drawView: nil];
    m_timestamp = CACurrentMediaTime();
   
    //CADisplayLink* displayLink;
    displayLink = [CADisplayLink displayLinkWithTarget:self
                                              selector:@selector(drawView:)];
   
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                      forMode:NSDefaultRunLoopMode];
}

- (void) resetdisplayLink
{
    displayLink = nil;
   
    m_applicationEngine->SetIconRotateValue(smallIconRotateXValue, smallIconRotateYValue, smallIconRotateZValue);
   
   
    [self drawView: nil];
   
    m_timestamp = CACurrentMediaTime();
   
    //CADisplayLink* displayLink;
    displayLink = [CADisplayLink displayLinkWithTarget:self
                                              selector:@selector(drawView:)];
   
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                      forMode:NSDefaultRunLoopMode];
}

.....

@end


5. Interfaces.hpp

#pragma once
#include "Vector.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <string>

using std::vector;
using std::string;

enum VertexFlags {
    VertexFlagsNormals = 1 << 0,  //  ==1
    VertexFlagsTexCoords = 1 << 1,  // ==2
};

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 void SetIconRotateValue(float x, float y, float z) =0;  // add for icon rotation
    virtual void setLightPos(float x, float y, float z) = 0; // add for light position
    virtual void setGLSL(int mode) =0;
    //virtual Quaternion get_m_orientation() =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 setLightPos(float x, float y, float z) =0; //const = 0;  /// 不能使用const
    virtual void setGLSL(int mode) =0;
    ///virtual Quaternion get_m_orientation () const =0;
    virtual ~IRenderingEngine() {}
};

// 此處的CreateApplicationEngine有重複到Function name,因此使用namespace
namespace ParametricViewer { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*); }
namespace SolidES1     { IRenderingEngine* CreateRenderingEngine(); }
namespace SolidES2     { IRenderingEngine* CreateRenderingEngine(); }
  

6. ApplicationEngine.ParametricViewer.cpp

#include "Interfaces.hpp"
#include "ParametricEquations.hpp"

using namespace std;

namespace ParametricViewer {
   
    static const int SurfaceCount = 6;
    static const int ButtonCount = SurfaceCount - 1;
   
    struct Animation {
        bool Active;
        float Elapsed;
        float Duration;
        Visual StartingVisuals[SurfaceCount];
        Visual EndingVisuals[SurfaceCount];
    };
   
    // ApplicationEngine 公開繼承 IApplicationEngine,
    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);
        void SetIconRotateValue(float x, float y, float z);
        void setLightPos(float x, float y, float z);
        void setGLSL(int mode);
        //Quaternion get_m_orientation();
        //void set_m_orientation(Quaternion in_orientation);
       
    private:
        void PopulateVisuals(Visual* visuals) const;
        int MapToButton(ivec2 touchpoint) const;
        vec3 MapToSphere(ivec2 touchpoint) const;
        float m_trackballRadius;
        ivec2 m_screenSize;
        ivec2 m_centerPoint;
        ivec2 m_fingerStart;
        bool m_spinning;
        Quaternion m_orientation;
        Quaternion m_previousOrientation;
        int m_currentSurface;
        ivec2 m_buttonSize;
        int m_pressedButton;
        int m_buttonSurfaces[ButtonCount];
        Animation m_animation;
        IRenderingEngine* m_renderingEngine;
       
        float  smallIconRotateXValue;
        float  smallIconRotateYValue;
        float  smallIconRotateZValue;
       
        float  lightPosX;
        float  lightPosY;
        float  lightPosZ;
       
        int    GLSL_Mode;
       
        //Quaternion  old_m_orientation;
       
    };
   
    .....
   
    void ApplicationEngine::Initialize(int width, int height)
    {
        m_trackballRadius = width / 3;
        m_buttonSize.y = height / 10;
        m_buttonSize.x = 4 * m_buttonSize.y / 3;
        m_screenSize = ivec2(width, height - m_buttonSize.y);
        m_centerPoint = m_screenSize / 2;
       
        vector<ISurface*> surfaces(SurfaceCount);
        surfaces[0] = new Cone(3, 1);  // 設定半徑及高,其他
        surfaces[1] = new Sphere(1.4f);
        surfaces[2] = new Torus(1.4f, 0.3f);
        surfaces[3] = new TrefoilKnot(1.8f);
        surfaces[4] = new KleinBottle(0.2f);
        surfaces[5] = new MobiusStrip(1);
       
        m_renderingEngine->setGLSL(GLSL_Mode);
       

        m_renderingEngine->Initialize(surfaces);
       
        for (int i = 0; i < SurfaceCount; i++)
            delete surfaces[i];
    }
   
    .....
       
    void ApplicationEngine::setGLSL(int mode)
    {
        GLSL_Mode = mode;
    }

   
   .....
   
}


7. RenderingEngine.WireframeES2.cpp
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <iostream>

namespace SolidES2 {
    //WireframeES2 {
   
#define STRINGIFY(A)  #A

#include "./Shaders/Simple.es2.vert"
#include "./Shaders/Simple.es2.frag"
   
#include "./Shaders/PixelLighting.es2.frag"
#include "./Shaders/PixelLighting.es2.vert"
   
#include "./Shaders/ToonShading.es2.frag"


    struct UniformHandles {  // new   專門處理 Simple.es2.vert 中 uniform 的變數
        GLuint Modelview;
        GLuint Projection;
        GLuint NormalMatrix;
        GLuint LightPosition;
        GLint AmbientMaterial;
        GLint SpecularMaterial;
        GLint Shininess;
    };
   
    struct AttributeHandles {   // new 專門處理 Simple.es2.vert 中 attribute 的變數
        GLint Position;
        GLint Normal;
        GLint DiffuseMaterial;
    };
   
    struct Drawable {
        GLuint VertexBuffer;
        GLuint IndexBuffer;
        int IndexCount;
    };
   
    class RenderingEngine : public IRenderingEngine {
    public:
        RenderingEngine();
        void Initialize(const vector<ISurface*>& surfaces);
        void Render(const vector<Visual>& visuals) const;
        void setLightPos(float x, float y, float z) ;  // const // 使用const就變成惟讀
        void setGLSL(int mode);
    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; //new and replace
        //GLint m_projectionUniform;
        //GLint m_modelviewUniform;
        //GLuint m_positionSlot;
        //GLuint m_colorSlot;
        mat4 m_translation;
       
        UniformHandles m_uniforms;   // new  改成用struct 模式來控制 GLSL 檔中的uniform 變數
        AttributeHandles m_attributes;  // new  改成用struct 模式來控制 GLSL 檔中的 attribute 變數

        float lightPosX;
        float lightPosY;
        float lightPosZ;
       
        int GLSL_Mode;
       
    };
   
    IRenderingEngine* CreateRenderingEngine()
    {
        return new RenderingEngine();
    }
   
    RenderingEngine::RenderingEngine()
    {
        glGenRenderbuffers(1, &m_colorRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
       
        GLSL_Mode = 0;
    }
   
    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;
           
            //(*surface)->GenerateVertices(vertices);
            (*surface)->GenerateVertices(vertices, VertexFlagsNormals);  // new / replace
            //Tell the ParametricSurface object that we need normals by passing in the new VertexFlagsNormals flag.
           
            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)->GetLineIndexCount();
            int indexCount = (*surface)->GetTriangleIndexCount();  // replace  改成三角形處理,數量不同
           
            GLuint indexBuffer;
            if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
                indexBuffer = m_drawables[0].IndexBuffer;
            } else {
                vector<GLushort> indices(indexCount);
               
                //(*surface)->GenerateLineIndices(indices);
                (*surface)->GenerateTriangleIndices(indices);  // replace  改成三角形處理,
               
               
                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); // 新增drawable至 m_drawables 的尾端,必要時會進行記憶體配置。
        }
       
        // Extract width and height.  new
        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.  new
        glGenRenderbuffers(1, &m_depthRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
       
        // Create the framebuffer object. (FBO)
        GLuint framebuffer;
        glGenFramebuffers(1, &framebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
       
        // 设置FrameBuffer并使用glFramebufferRenderBuffer相互关联
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, //
                                  GL_RENDERBUFFER, m_colorRenderbuffer);
       
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,   // new
                                  GL_RENDERBUFFER, m_depthRenderbuffer);
       
        glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
       
       
        // Create the GLSL program.  // replace
       
        GLuint program;
       
        if (GLSL_Mode == 0)
            program = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
        else if (GLSL_Mode ==1)
            program = BuildProgram(SimpleVertexShader4Pixel, SimpleFragmentShader4Pixel);
        else
            program = BuildProgram(SimpleVertexShader4Pixel, ToonShader);
       

       
        glUseProgram(program);
       
        // Create the GLSL program.
        //GLuint simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
        //glUseProgram(simpleProgram);
       
       
//        m_positionSlot = glGetAttribLocation(simpleProgram, "Position");
//        m_colorSlot = glGetAttribLocation(simpleProgram, "SourceColor");
//        glEnableVertexAttribArray(m_positionSlot);
//       

       
        // Extract the handles to attributes and uniforms.  replace
        m_attributes.Position = glGetAttribLocation(program, "Position");
        m_attributes.Normal = glGetAttribLocation(program, "Normal");
        m_attributes.DiffuseMaterial = glGetAttribLocation(program, "DiffuseMaterial");
       
        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");
       
        // Set up some default material parameters.  // replace
        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);
       
        //        // Set up some matrices.
        //        m_translation = mat4::Translate(0, 0, -7);
        //        m_projectionUniform = glGetUniformLocation(simpleProgram, "Projection");
        //        m_modelviewUniform = glGetUniformLocation(simpleProgram, "Modelview");
       
        // Initialize various state.  // replace
        glEnableVertexAttribArray(m_attributes.Position);
        glEnableVertexAttribArray(m_attributes.Normal);
        glEnable(GL_DEPTH_TEST);
       
        // Set up transforms.   (change line position)
        m_translation = mat4::Translate(0, 0, -7);

    }
   
    .....
      
    void RenderingEngine::setGLSL(int mode)  // const // 使用const就變成惟讀
    {
        GLSL_Mode = mode;
    }


}




8.結果比較,分別是@"vertex lighting", @"pixel lighting", @"Toon Shading",其中pixel lighting看起來真實感較佳。




2013年6月12日 星期三

OpenGL基本實作(十)

根據OpenGL基本實作(九)的專案,修改其中表面的材質,並加入光照,如此就與iphone 3D Programm上的例子相當了,為了方便觀看,把整個程式改成LandScape方向顯示。除此之外,並大幅修改原始的程式,加入了六個Slider,右上的三個分別控制光照的X,Y,Z方位,範圍從-100~100。而其他的三個則控制小圖的物件方位,分別轉動X,Y,Z三個方向,範圍從-180~180度。這樣可以更瞭解光照與物件轉動的含義。

1. 首先開啓了一個新的專案

OpenGL基本實作(九)的各個檔案複製過來,會如此作,實在是無法完成一個xcode的專案複製,並且修改專案的名稱,因此使用最愚蠢的方法了。其中的裡面的其他檔案名稱倒是不必更改。



  

2.  從上向下修改,首先從GLSL的Simple.es2.vert開始

static const char* SimpleVertexShader = STRINGIFY(

//To keep things simple, we'll use the infinite light source model for diffuse combined with the infinite viewer model for specular. We'll also assume that the light is white.

//vec3 ComputeLighting(vec3 normal)
//{
//N = NormalMatrix * normal
//L = Normalize(LightPosition)
//E = (0, 0, 1)    
當觀眾在無限遠處時,E可以簡化成  [0, 0, 1] 
//H = Normalize(L + E)
//df = max(0, N ∙ L)    
(df)DiffuseFactor = max(0, dot(N, L))      ***  0 <= df <= 1//sf = max(0, N ∙ H)
//sf = sf ^ Shininess


Specular Lighting         

//return AmbientMaterial + DiffuseMaterial * df + SpecularMaterial * sf
//}

//  原來的項目,已不需要了,不再需要設定線條的顏色
//attribute vec4 SourceColor;

attribute vec4 Position;    // 原來的項目

uniform mat4 Projection;     // 原來的項目
uniform mat4 Modelview;      // 原來的項目

varying vec4 DestinationColor;  // 原來的項目

// 以下是光照項目的變數
attribute vec3 Normal;     // 法線
attribute vec3 DiffuseMaterial;  // 瀰漫

uniform mat3 NormalMatrix;    // 法線矩陣
uniform vec3 LightPosition;      // 照射位置
uniform vec3 AmbientMaterial;   // 環境
uniform vec3 SpecularMaterial;   // 鏡面
uniform float Shininess;             // 反光


//use uniforms to store light position, specular and ambient properties.

void main(void)
{

    vec3 N = NormalMatrix * Normal;
    vec3 L = normalize(LightPosition);
    vec3 E = vec3(0, 0, 1);
    vec3 H = normalize(L + E);

    float df = max(0.0, dot(N, L));
    float sf = max(0.0, dot(N, H));
    sf = pow(sf, Shininess);

    vec3 color = AmbientMaterial + df * DiffuseMaterial + sf * SpecularMaterial;

    //DestinationColor = SourceColor;
    DestinationColor = vec4(color, 1);  // replace

    gl_Position = Projection * Modelview * Position;
}
);


3.mainViewController.mm加入六個Slider來作為調整光照的位置及小圖示的轉動方向。


#import "mainViewController.h"

@interface mainViewController ()

@end

@implementation mainViewController
{

    UISlider  *sIconXRotateSlider;
    UISlider  *sIconYRotateSlider;
    UISlider  *sIconZRotateSlider;
   
    UISlider  *lightPosXSlider;
    UISlider  *lightPosYSlider;
    UISlider  *lightPosZSlider;

   
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    CGRect screenBounds = [[UIScreen mainScreen] bounds];
   
    m_window = [[UIWindow alloc] initWithFrame: screenBounds];
   
    m_view = [[GLView alloc] initWithFrame: screenBounds];

    [m_window addSubview: m_view];
    [m_window makeKeyAndVisible];
   
    [self setSlideInterface1];
    [self setSlideInterface2];
    [self setSlideInterface3];
   
    [self setSlideInterfaceLightX];
    [self setSlideInterfaceLightY];
    [self setSlideInterfaceLightZ];
   
   
    [m_window addSubview:sIconXRotateSlider];
    [m_window addSubview:sIconYRotateSlider];
    [m_window addSubview:sIconZRotateSlider];
   
    [m_window addSubview:lightPosXSlider];
    [m_window addSubview:lightPosYSlider];
    [m_window addSubview:lightPosZSlider];

 }

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(BOOL)shouldAutorotate{
    return YES;
}

// 轉到Landscape 模式
-(NSInteger)supportedInterfaceOrientations{
   
    //    UIInterfaceOrientationMaskLandscape;
    //    24
    //
    //    UIInterfaceOrientationMaskLandscapeLeft;
    //    16
    //
    //    UIInterfaceOrientationMaskLandscapeRight;
    //    8
    //
    //    UIInterfaceOrientationMaskPortrait;
    //    2
   
    //    return UIInterfaceOrientationMaskPortrait;
    //    or
    return 24;
}

- (void)setSlideInterface1 {
   
    sIconXRotateSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
       
    [sIconXRotateSlider setCenter:CGPointMake(50, 550)];//位置放在x=50, y=550的位置
   
   
    [sIconXRotateSlider addTarget:self action:@selector(onSlider1ValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    sIconXRotateSlider.minimumValue = -1;
    sIconXRotateSlider.maximumValue = 1;
    sIconXRotateSlider.continuous = YES;
   
    [sIconXRotateSlider setValue:0];
   
    [self.view addSubview:sIconXRotateSlider];
   
}

- (void) onSlider1ValueChange
{
    float myValue = sIconXRotateSlider.value;
   
    m_view->smallIconRotateXValue = myValue;
   
    //[m_view initSet:m_window.frame];
    [m_view resetdisplayLink];
}


- (void)setSlideInterface2 {
   
    sIconYRotateSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
   
    [sIconYRotateSlider setCenter:CGPointMake(50, 650)];//位置放在x=50, y=650的位置
   
   
    [sIconYRotateSlider addTarget:self action:@selector(onSlider2ValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    sIconYRotateSlider.minimumValue = -1;
    sIconYRotateSlider.maximumValue = 1;
    sIconYRotateSlider.continuous = YES;
   
    [sIconYRotateSlider setValue:0];
   
    [self.view addSubview:sIconYRotateSlider];
   
}

- (void) onSlider2ValueChange
{
    float myValue = sIconYRotateSlider.value;
   
    m_view->smallIconRotateYValue = myValue;
   
    //[m_view initSet:m_window.frame];
    [m_view resetdisplayLink];
}


- (void)setSlideInterface3 {
   
    sIconZRotateSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
   
    [sIconZRotateSlider setCenter:CGPointMake(50, 750)];//位置放在x=50, y=750的位置
   
   
    [sIconZRotateSlider addTarget:self action:@selector(onSlider3ValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    sIconZRotateSlider.minimumValue = -1;
    sIconZRotateSlider.maximumValue = 1;
    sIconZRotateSlider.continuous = YES;
   
    [sIconZRotateSlider setValue:0];
   
    [self.view addSubview:sIconZRotateSlider];
   
}

- (void) onSlider3ValueChange
{
    float myValue = sIconZRotateSlider.value;
   
    m_view->smallIconRotateZValue = myValue;
   
    //[m_view initSet:m_window.frame];
    [m_view resetdisplayLink];
}



- (void)setSlideInterfaceLightX{
   
    lightPosXSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 200, 50)];
   
    [lightPosXSlider setCenter:CGPointMake(600, 850)];//位置放在x=600, y=850的位置
   
   
    [lightPosXSlider addTarget:self action:@selector(onSliderLinghtXValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    lightPosXSlider.minimumValue = -100;
    lightPosXSlider.maximumValue = 100;
    lightPosXSlider.continuous = YES;
   
    [lightPosXSlider setValue:0.25];
   
    [self.view addSubview:lightPosXSlider];
   
}

- (void) onSliderLinghtXValueChange
{
    float myValue = lightPosXSlider.value;
   
    m_view->lightPosX = myValue;
   
    //[m_view initSet:m_window.frame];
   
    [m_view resetdisplayLink];
   
}

- (void)setSlideInterfaceLightY{
   
    lightPosYSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 200, 50)];
   
    [lightPosYSlider setCenter:CGPointMake(600, 900)];//位置放在x=600, y=900的位置
   
   
    [lightPosYSlider addTarget:self action:@selector(onSliderLinghtYValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    lightPosYSlider.minimumValue = -100;
    lightPosYSlider.maximumValue = 100;
    lightPosYSlider.continuous = YES;
   
    [lightPosYSlider setValue:0.25];
   
    [self.view addSubview:lightPosYSlider];
   
}

- (void) onSliderLinghtYValueChange
{
    float myValue = lightPosYSlider.value;
   
    m_view->lightPosY = myValue;
   
    //[m_view initSet:m_window.frame];
   
    [m_view resetdisplayLink];
   
}

- (void)setSlideInterfaceLightZ{
   
    lightPosZSlider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 200, 50)];
   
    [lightPosZSlider setCenter:CGPointMake(600, 950)];//位置放在x=600, y=950的位置
   
   
    [lightPosZSlider addTarget:self action:@selector(onSliderLinghtZValueChange) forControlEvents:UIControlEventValueChanged];
   
   
    lightPosZSlider.minimumValue = -100;
    lightPosZSlider.maximumValue = 100;
    lightPosZSlider.continuous = YES;
   
    [lightPosZSlider setValue:1];
   
    [self.view addSubview:lightPosZSlider];
   
}

- (void) onSliderLinghtZValueChange
{
    float myValue = lightPosZSlider.value;
   
    m_view->lightPosZ = myValue;
   
    //[m_view initSet:m_window.frame];
   
    [m_view resetdisplayLink];
   
}


@end


4. GLView.h 加入Slider對應的變數

#import <UIKit/UIKit.h>

#import "Interfaces.hpp"
#import <QuartzCore/QuartzCore.h>

@interface GLView : UIView
{
@private
    IApplicationEngine* m_applicationEngine;
    IRenderingEngine* m_renderingEngine;
    EAGLContext* m_context;
    float m_timestamp;
   
@public
    float smallIconRotateXValue;
    float smallIconRotateYValue;
    float smallIconRotateZValue;
   
    float  lightPosX;
    float  lightPosY;
    float  lightPosZ;
   
    Quaternion old_orientation;
   
    CADisplayLink* displayLink;

}

- (void) drawView: (CADisplayLink*) displayLink;

- (id) initSet:(CGRect) frame;

- (void) resetdisplayLink;

@end





5. GLView.mm 加入變數對應的控制碼


#import "GLView.h"

@implementation GLView
{

}

+ (Class) layerClass
{
    return [CAEAGLLayer class];
}

- (id) initWithFrame: (CGRect) frame
{
    smallIconRotateXValue =0;
    smallIconRotateYValue =0;
    smallIconRotateZValue =0;
   
    lightPosX =0.25;
    lightPosY =0.25;
    lightPosZ =1;
   
    old_orientation = Quaternion(0, 0, 0, 1);

   
    if (self = [super initWithFrame:frame])
    {
        if  ([self initSet:frame] == nil)
            return nil;
    }
    return self;
}

- (id) initSet:(CGRect) frame
{
    CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
    eaglLayer.opaque = YES;
   
    //EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    EAGLRenderingAPI 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 = WireframeES1::CreateRenderingEngine();
        m_renderingEngine = SolidES1::CreateRenderingEngine();
    } else {
        NSLog(@"Using OpenGL ES 2.0");
        //m_renderingEngine = WireframeES2::CreateRenderingEngine(); // 完成 m_colorRenderbuffer 設定
       
        m_renderingEngine = SolidES2::CreateRenderingEngine();  // replace
    }
   
    m_applicationEngine = ParametricViewer::CreateApplicationEngine(m_renderingEngine);
   
    m_applicationEngine->SetIconRotateValue(smallIconRotateXValue, smallIconRotateYValue, smallIconRotateZValue);

   
    [m_context
     renderbufferStorage:GL_RENDERBUFFER
     fromDrawable: eaglLayer];
   
    int width = CGRectGetWidth(frame);
    int height = CGRectGetHeight(frame);
    m_applicationEngine->Initialize(width, height);
       
    [self setdisplayLink];
   

    return self;
}

- (void) setdisplayLink
{
    [self drawView: nil];
    m_timestamp = CACurrentMediaTime();
   
    //CADisplayLink* displayLink;
    displayLink = [CADisplayLink displayLinkWithTarget:self
                                              selector:@selector(drawView:)];
   
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                      forMode:NSDefaultRunLoopMode];
}


- (void) resetdisplayLink
{
    displayLink = nil;
   
    m_applicationEngine->SetIconRotateValue(smallIconRotateXValue, smallIconRotateYValue, smallIconRotateZValue);
   
   
    [self drawView: nil];
   
    m_timestamp = CACurrentMediaTime();
   
    //CADisplayLink* displayLink;
    displayLink = [CADisplayLink displayLinkWithTarget:self
                                              selector:@selector(drawView:)];
   
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                      forMode:NSDefaultRunLoopMode];
}



- (void) drawView: (CADisplayLink*) displayLink
{
    if (displayLink != nil) {
       
        m_applicationEngine->setLightPos(lightPosX, lightPosY, lightPosZ);       
        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));
}

- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint location  = [touch locationInView: self];
    m_applicationEngine->OnFingerUp(ivec2(location.x, location.y));  //傳入所按的平面位置
}

- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{
    UITouch* touch = [touches anyObject];
    CGPoint previous  = [touch previousLocationInView: self];
    CGPoint current = [touch locationInView: self];
    m_applicationEngine->OnFingerMove(ivec2(previous.x, previous.y),
                                      ivec2(current.x, current.y));
}


@end




6. Interfaces.hpp 加入所需要的虛擬函數

#pragma once
#include "Vector.hpp"
#include "Quaternion.hpp"
#include <vector>
#include <string>

using std::vector;
using std::string;

enum VertexFlags {
    VertexFlagsNormals = 1 << 0,  //  ==1
    VertexFlagsTexCoords = 1 << 1,  // ==2
};

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 void SetIconRotateValue(float x, float y, float z) =0;  // add for icon rotation
    virtual void setLightPos(float x, float y, float z) = 0; // add for light position

    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 setLightPos(float x, float y, float z) =0; //const = 0;  /// 不能使用const
    virtual ~IRenderingEngine() {}
};

// 此處的CreateApplicationEngine有重複到Function name,因此使用namespace
namespace ParametricViewer { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*); }
//namespace ObjViewer    { IApplicationEngine* CreateApplicationEngine(IRenderingEngine*, IResourceManager*); }
//namespace Darwin       { IResourceManager* CreateResourceManager(); }
//namespace WireframeES1 { IRenderingEngine* CreateRenderingEngine(); }
//namespace WireframeES2 { IRenderingEngine* CreateRenderingEngine(); }

namespace SolidES1     { IRenderingEngine* CreateRenderingEngine(); }
namespace SolidES2     { IRenderingEngine* CreateRenderingEngine(); }



7. ApplicationEngine.ParametricViewer.cpp 計算放置3D Object的位置/方位及顏色


#include "Interfaces.hpp"
#include "ParametricEquations.hpp"

using namespace std;

namespace ParametricViewer {
  
    static const int SurfaceCount = 6;
    static const int ButtonCount = SurfaceCount - 1;
  
    struct Animation {
        bool Active;
        float Elapsed;
        float Duration;
        Visual StartingVisuals[SurfaceCount];
        Visual EndingVisuals[SurfaceCount];
    };
  
    // ApplicationEngine 公開繼承 IApplicationEngine,
    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);
        void SetIconRotateValue(float x, float y, float z);
        void setLightPos(float x, float y, float z);
             
    private:
        void PopulateVisuals(Visual* visuals) const;
        int MapToButton(ivec2 touchpoint) const;
        vec3 MapToSphere(ivec2 touchpoint) const;
        float m_trackballRadius;
        ivec2 m_screenSize;
        ivec2 m_centerPoint;
        ivec2 m_fingerStart;
        bool m_spinning;
        Quaternion m_orientation;
        Quaternion m_previousOrientation;
        int m_currentSurface;
        ivec2 m_buttonSize;
        int m_pressedButton;
        int m_buttonSurfaces[ButtonCount];
        Animation m_animation;
        IRenderingEngine* m_renderingEngine;
      
        float  smallIconRotateXValue;
        float  smallIconRotateYValue;
        float  smallIconRotateZValue;
      
        float  lightPosX;
        float  lightPosY;
        float  lightPosZ;

              
    };
  
    IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
    {
        return new ApplicationEngine(renderingEngine);
    }
  
    ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
    m_spinning(false),
    m_pressedButton(-1),
    m_renderingEngine(renderingEngine)
    {
        m_animation.Active = false;
        m_buttonSurfaces[0] = 0;
        m_buttonSurfaces[1] = 1;
        m_buttonSurfaces[2] = 4;
        m_buttonSurfaces[3] = 3;
        m_buttonSurfaces[4] = 2;
        m_currentSurface = 5;
    }
  
    ApplicationEngine::~ApplicationEngine()
    {
        delete m_renderingEngine;
    }
  
    void ApplicationEngine::Initialize(int width, int height)
    {
        m_trackballRadius = width / 3;
        m_buttonSize.y = height / 10;
        m_buttonSize.x = 4 * m_buttonSize.y / 3;
        m_screenSize = ivec2(width, height - m_buttonSize.y);
        m_centerPoint = m_screenSize / 2;
      
        vector<ISurface*> surfaces(SurfaceCount);
        surfaces[0] = new Cone(3, 1);  // 設定半徑及高,其他
        surfaces[1] = new Sphere(1.4f);
        surfaces[2] = new Torus(1.4f, 0.3f);
        surfaces[3] = new TrefoilKnot(1.8f);
        surfaces[4] = new KleinBottle(0.2f);
        surfaces[5] = new MobiusStrip(1);
        m_renderingEngine->Initialize(surfaces);
        for (int i = 0; i < SurfaceCount; i++)
            delete surfaces[i];
    }
  
    void ApplicationEngine::PopulateVisuals(Visual* visuals) const
    {
        //設定所有圖像的顏色與大小,包含主圖及Button
        for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
          
            int visualIndex = m_buttonSurfaces[buttonIndex];
            visuals[visualIndex].Color = vec3(0.25f*3, 0.25f, 0.25f);  // Button上的顏色
            if (m_pressedButton == buttonIndex)
                visuals[visualIndex].Color = vec3(0.5f, 0.5f*3, 0.5f);  // 點選後的Button顏色
          
            // 設定每一個Button上的圖案大小
//            visuals[visualIndex].ViewportSize = m_buttonSize;
//            visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
//            visuals[visualIndex].LowerLeft.y = 0;
//            visuals[visualIndex].Orientation = Quaternion(); // 基本button的旋轉值為0
          
            visuals[visualIndex].ViewportSize = m_buttonSize;
            visuals[visualIndex].LowerLeft.x = 0;
            visuals[visualIndex].LowerLeft.y = m_screenSize.y - buttonIndex * m_buttonSize.y ;
            //visuals[visualIndex].Orientation = Quaternion(0,0,0,1); // 基本button的旋轉值為0

          
          
            float angleValueX = smallIconRotateXValue;  // -1 ~ 1 , 負值為順時鐘
            float angleX = M_PI_2 *angleValueX;
          
            float sinx = sin(angleX);
            float cosx = cos(angleX);
          
            float angleValueY = smallIconRotateYValue;  // -1 ~ 1 , 負值為順時鐘
            float angleY = M_PI_2 *angleValueY;
          
            float siny = sin(angleY);
            float cosy = cos(angleY);
          
            float angleValueZ = smallIconRotateZValue;  // -1 ~ 1 , 負值為順時鐘
            float angleZ = M_PI_2 *angleValueZ;
          
            float sinz = sin(angleZ);
            float cosz = cos(angleZ);
          
            Quaternion x = Quaternion(sinx*1,sinx*0, sinx*0, cosx);
            Quaternion y = Quaternion(siny*0,siny*1, siny*0, cosy);
            Quaternion z = Quaternion(sinz*0,sinz*0, sinz*1, cosz);
          
            Quaternion xy = x.Rotated(y);
            Quaternion xyz = xy.Rotated(z);
      
            visuals[visualIndex].Orientation = xyz;

        }
      
        // 顯示主圖的顏色,m_spinning代表手指按下的狀態,蓋掉前面所設的值
        visuals[m_currentSurface].Color = m_spinning ? vec3(1, 0, 0.75f) : vec3(1*3, 1, 0.5f);
        visuals[m_currentSurface].LowerLeft = ivec2(0, m_buttonSize.y);
        visuals[m_currentSurface].ViewportSize = ivec2(m_screenSize.x, m_screenSize.y);
        visuals[m_currentSurface].Orientation = m_orientation; //主圖的旋轉值
    }
  
    void ApplicationEngine::Render() const
    {
        vector<Visual> visuals(SurfaceCount);
      
        if (!m_animation.Active) {
            PopulateVisuals(&visuals[0]);
        } else {
            float t = m_animation.Elapsed / m_animation.Duration;
          
            for (int i = 0; i < SurfaceCount; i++) {
              
                // 找出起始的visuals[x] 及最後的visuals[y]
                const Visual& start = m_animation.StartingVisuals[i];
                const Visual& end = m_animation.EndingVisuals[i];
              
                Visual& tweened = visuals[i];  // 這時的 visuals[]中是空的
              
                // 以下將所有的值重新填到新的visuals[]中,根據現在所見的狀態
                tweened.Color = start.Color.Lerp(t, end.Color); //將顏色做線性插補取得時間變化值
                tweened.LowerLeft = start.LowerLeft.Lerp(t, end.LowerLeft);
                tweened.ViewportSize = start.ViewportSize.Lerp(t, end.ViewportSize);
                tweened.Orientation = start.Orientation.Slerp(t, end.Orientation);
            }
        }
      
        m_renderingEngine->setLightPos(lightPosX,lightPosY,lightPosZ);
      
        m_renderingEngine->Render(visuals);
    }
  
    void ApplicationEngine::UpdateAnimation(float dt)
    {
        if (m_animation.Active) {  // 改選成另一個物件
            m_animation.Elapsed += dt;
            if (m_animation.Elapsed > m_animation.Duration)
                m_animation.Active = false;
        }
    }
  
    void ApplicationEngine::SetIconRotateValue(float x, float y , float z)
    {
        smallIconRotateXValue = x;
        smallIconRotateYValue = y;
        smallIconRotateZValue = z;
    }
  
    void ApplicationEngine::setLightPos(float x, float y , float z)
    {
        lightPosX = x;
        lightPosY = y;
        lightPosZ = z;
    }

   
    // 3. 手指離開
    void ApplicationEngine::OnFingerUp(ivec2 location)
    {
        m_spinning = false;
      
        if (m_pressedButton != -1 && m_pressedButton == MapToButton(location) &&
            !m_animation.Active)  // 如果按選了其他的物件,就進行以下的程序
        {
            m_animation.Active = true;
            m_animation.Elapsed = 0;
            m_animation.Duration = 0.25f;
          
            PopulateVisuals(&m_animation.StartingVisuals[0]);
            swap(m_buttonSurfaces[m_pressedButton], m_currentSurface); // 點選的Button圖像與主圖像交換
            PopulateVisuals(&m_animation.EndingVisuals[0]);
        }
      
        m_pressedButton = -1;
    }
  
    //  1. 壓下手指
    void ApplicationEngine::OnFingerDown(ivec2 location)
    {
        m_fingerStart = location;
        m_previousOrientation = m_orientation; // 取得現在的旋轉值
        m_pressedButton = MapToButton(location);
        if (m_pressedButton == -1)
            m_spinning = true;
    }
  
    // 2. 移動手指讓物件轉動
    void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
    {
        if (m_spinning) {
            vec3 start = MapToSphere(m_fingerStart);
            vec3 end = MapToSphere(location);
            Quaternion delta = Quaternion::CreateFromVectors(start, end); // 取得方向向量
            m_orientation = delta.Rotated(m_previousOrientation); // 根據前一個旋轉值,計算再次旋轉後的值,此為主圖所用
        }
      
        if (m_pressedButton != -1 && m_pressedButton != MapToButton(location))
            m_pressedButton = -1;
    }
  
    // 確認所按的位置為主圖像的位置,並回傳所按的點,經計算後的3D位置。
    vec3 ApplicationEngine::MapToSphere(ivec2 touchpoint) const
    {
        vec2 p = touchpoint - m_centerPoint;
      
        // Flip the Y axis because pixel coords increase towards the bottom.
        p.y = -p.y;
      
        const float radius = m_trackballRadius;
        const float safeRadius = radius - 1;
      
        if (p.Length() > safeRadius) {
            float theta = atan2(p.y, p.x);
            p.x = safeRadius * cos(theta);
            p.y = safeRadius * sin(theta);
        }
      
        float z = sqrt(radius * radius - p.LengthSquared());
        vec3 mapped = vec3(p.x, p.y, z);
        return mapped / radius;
    }
  
    /*
    // 確認按到了Button 的位置,並回傳所按的Button代號。原始的
    int ApplicationEngine::MapToButton(ivec2 touchpoint) const
    {
        if (touchpoint.y  < m_screenSize.y - m_buttonSize.y)
            return -1;
      
        int buttonIndex = touchpoint.x / m_buttonSize.x;
        if (buttonIndex >= ButtonCount)
            return -1;
      
        return buttonIndex;
    }
    */
  
    // 確認按到了Button 的位置,並回傳所按的Button代號。 修改後給 landscape用
    int ApplicationEngine::MapToButton(ivec2 touchpoint) const
    {
        if (touchpoint.x  >  m_buttonSize.x)
            return -1;
      
        //m_screenSize.y - buttonIndex * m_buttonSize.y ;
        int buttonIndex = (touchpoint.y) / m_buttonSize.y;
        if (buttonIndex >= ButtonCount)
            return -1;
      
        return buttonIndex;
    }

  
}


8. RenderingEngine.WireframeES1.cpp  OPENGLES V1.1的主要呼叫程式碼


#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"

//namespace WireframeES1 {
namespace SolidES1 {     // replace

   
    struct Drawable {
        GLuint VertexBuffer;
        GLuint IndexBuffer;
        int IndexCount;
    };
   
    class RenderingEngine : public IRenderingEngine {
    public:
        RenderingEngine();
        void Initialize(const vector<ISurface*>& surfaces);
        void Render(const vector<Visual>& visuals) const;
        void setLightPos(float x, float y, float z); // const // 使用const就變成惟讀
       
    private:
        vector<Drawable> m_drawables;
        GLuint m_colorRenderbuffer;
        GLuint m_depthRenderbuffer; // new var
        mat4 m_translation;
       
        float lightPosX;
        float lightPosY;
        float lightPosZ;

       
    };
   
    IRenderingEngine* CreateRenderingEngine()
    {
        return new RenderingEngine();
    }
   
    RenderingEngine::RenderingEngine()
    {
        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;
            //(*surface)->GenerateVertices(vertices);
            (*surface)->GenerateVertices(vertices, VertexFlagsNormals); // replace

           
            GLuint vertexBuffer;
            glGenBuffers(1, &vertexBuffer);  //設定GPU memory 給 Vertex用
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
            glBufferData(GL_ARRAY_BUFFER,   // 將vertex資料存到GPU memory
                         vertices.size() * sizeof(vertices[0]),  // 確認記憶體大小
                         &vertices[0],
                         GL_STATIC_DRAW);
           
            // Create a new VBO for the indices if needed.
            //int indexCount = (*surface)->GetLineIndexCount();
            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)->GenerateLineIndices(indices);  // 4個 indices為一組
                (*surface)->GenerateTriangleIndices(indices);  // replace

               
                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.  new
        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.  new
        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,  // new
                                     GL_RENDERBUFFER_OES, m_depthRenderbuffer);

       
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer);
       
       
        // Set up various GL state.
        glEnableClientState(GL_VERTEX_ARRAY);
       
        glEnableClientState(GL_NORMAL_ARRAY); // new
        glEnable(GL_LIGHTING);   // new
        glEnable(GL_LIGHT0);     // new
        glEnable(GL_DEPTH_TEST);   // new
       
        // Set up the material properties.  // new
        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);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // replace   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.  // new
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            //vec4 lightPosition(0.25, 0.25, 1, 0);
            vec4 lightPosition(lightPosX,lightPosY, lightPosZ,0);
            glLightfv(GL_LIGHT0, GL_POSITION, lightPosition.Pointer());

           
            // Set the model-view transform.
            mat4 rotation = visual->Orientation.ToMatrix();
            mat4 modelview = rotation * m_translation;
            glMatrixMode(GL_MODELVIEW); //指定哪一个矩阵是当前矩阵, GL_MODELVIEW/GL_PROJECTION/GL_TEXTURE
            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.  //new
            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 wireframe.
//            int stride = sizeof(vec3);
//            const Drawable& drawable = m_drawables[visualIndex];
//            glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
//            glVertexPointer(3, GL_FLOAT, stride, 0);
//            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
//            glDrawElements(GL_LINES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
           
            // Draw the surface.  // new and renew
            int stride = 2 * sizeof(vec3);
            const GLvoid* normalOffset = (const GLvoid*) sizeof(vec3);
            const Drawable& drawable = m_drawables[visualIndex];
            glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
            glVertexPointer(3, GL_FLOAT, stride, 0);
            glNormalPointer(GL_FLOAT, stride, normalOffset);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
            glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
        }

    }
   
void RenderingEngine::setLightPos(float x, float y, float z)  // const // 使用const就變成惟讀函數
    {
        lightPosX = x;
        lightPosY = y;
        lightPosZ = z;
    }

   
}





9. RenderingEngine.WireframeES2.cpp  OPENGLES V2.0的主要呼叫程式碼

#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "Interfaces.hpp"
#include "Matrix.hpp"
#include <iostream>

namespace SolidES2 {
    //WireframeES2 {

   
#define STRINGIFY(A)  #A

#include "./Shaders/Simple.es2.vert"
#include "./Shaders/Simple.es2.frag"

    struct UniformHandles {  // new   專門處理 Simple.es2.vert 中 uniform 的變數
        GLuint Modelview;
        GLuint Projection;
        GLuint NormalMatrix;
        GLuint LightPosition;
        GLint AmbientMaterial;
        GLint SpecularMaterial;
        GLint Shininess;
    };
   
    struct AttributeHandles {   // new 專門處理 Simple.es2.vert 中 attribute 的變數
        GLint Position;
        GLint Normal;
        GLint DiffuseMaterial;
    };

   
    struct Drawable {
        GLuint VertexBuffer;
        GLuint IndexBuffer;
        int IndexCount;
    };
   
    class RenderingEngine : public IRenderingEngine {
    public:
        RenderingEngine();
        void Initialize(const vector<ISurface*>& surfaces);
        void Render(const vector<Visual>& visuals) const;
        void setLightPos(float x, float y, float z) ;  // const // 使用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; //new and replace
        //GLint m_projectionUniform;
        //GLint m_modelviewUniform;
        //GLuint m_positionSlot;
        //GLuint m_colorSlot;

        mat4 m_translation;
       
        UniformHandles m_uniforms;   // new  改成用struct 模式來控制 GLSL 檔中的uniform 變數
        AttributeHandles m_attributes;  // new  改成用struct 模式來控制 GLSL 檔中的 attribute 變數

        float lightPosX;
        float lightPosY;
        float lightPosZ;

       
    };
   
    IRenderingEngine* CreateRenderingEngine()
    {
        return new RenderingEngine();
    }
   
    RenderingEngine::RenderingEngine()
    {
        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;
           
            //(*surface)->GenerateVertices(vertices);
            (*surface)->GenerateVertices(vertices, VertexFlagsNormals);  // new / replace
            //Tell the ParametricSurface object that we need normals by passing in the new VertexFlagsNormals flag.

           
            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)->GetLineIndexCount();
            int indexCount = (*surface)->GetTriangleIndexCount();  // replace  改成三角形處理,數量不同

           
            GLuint indexBuffer;
            if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
                indexBuffer = m_drawables[0].IndexBuffer;
            } else {
                vector<GLushort> indices(indexCount);
               
                //(*surface)->GenerateLineIndices(indices);
                (*surface)->GenerateTriangleIndices(indices);  // replace  改成三角形處理,
               

                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); // 新增drawable至 m_drawables 的尾端,必要時會進行記憶體配置。
        }
       
        // Extract width and height.  new
        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.  new
        glGenRenderbuffers(1, &m_depthRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);

       
        // Create the framebuffer object. (FBO)
        GLuint framebuffer;
        glGenFramebuffers(1, &framebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
       
        // 设置FrameBuffer并使用glFramebufferRenderBuffer相互关联
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, //
                                  GL_RENDERBUFFER, m_colorRenderbuffer);
       
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,   // new
                                  GL_RENDERBUFFER, m_depthRenderbuffer);

       
        glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
       
       
        // Create the GLSL program.  // replace
        GLuint program = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
        glUseProgram(program);
       
        // Create the GLSL program.
        //GLuint simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
        //glUseProgram(simpleProgram);

       
       
//        m_positionSlot = glGetAttribLocation(simpleProgram, "Position");
//        m_colorSlot = glGetAttribLocation(simpleProgram, "SourceColor");
//        glEnableVertexAttribArray(m_positionSlot);
//       


        // Extract the handles to attributes and uniforms.  replace
        m_attributes.Position = glGetAttribLocation(program, "Position");
        m_attributes.Normal = glGetAttribLocation(program, "Normal");
        m_attributes.DiffuseMaterial = glGetAttribLocation(program, "DiffuseMaterial");
       

        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");

       
        // Set up some default material parameters.  // replace
        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);
       
        //        // Set up some matrices.
        //        m_translation = mat4::Translate(0, 0, -7);
        //        m_projectionUniform = glGetUniformLocation(simpleProgram, "Projection");
        //        m_modelviewUniform = glGetUniformLocation(simpleProgram, "Modelview");
       
        // Initialize various state.  // replace
        glEnableVertexAttribArray(m_attributes.Position);
        glEnableVertexAttribArray(m_attributes.Normal);
        glEnable(GL_DEPTH_TEST);
       

        // Set up transforms.   (change line position)
        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);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // replace
       

        vector<Visual>::const_iterator visual = visuals.begin();
       
        // 將所有的圖像都畫出,Button上的圖像,使用相對小的Size(64,48), 主圖大小為(320,432)
        // 0~5, 5指的是主畫面
        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.  // new
            //vec4 lightPosition(0.25, 0.25, 1, 0);
            vec4 lightPosition(lightPosX,lightPosY, lightPosZ,0);
            glUniform3fv(m_uniforms.LightPosition, 1, lightPosition.Pointer());

           
            // Set the model-view transform.
            mat4 rotation = visual->Orientation.ToMatrix();  // 只有主圖像的旋轉四元值被讀入,並轉為矩陣。
            mat4 modelview = rotation * m_translation;   // m_translation  =[0,0, -7]
            //glUniformMatrix4fv(m_modelviewUniform, 1, 0, modelview.Pointer());
            glUniformMatrix4fv(m_uniforms.Modelview, 1, 0, modelview.Pointer());  // replace
           

            // Set the normal matrix.  new
            // It's orthogonal, so its Inverse-Transpose is itself!
            mat3 normalMatrix = modelview.ToMat3();
            glUniformMatrix3fv(m_uniforms.NormalMatrix, 1, 0, normalMatrix.Pointer());

           
            // Set the projection transform.
            float h = 4.0f * size.y / size.x;
            mat4 projectionMatrix = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 10);
            //glUniformMatrix4fv(m_projectionUniform, 1, 0, projectionMatrix.Pointer());
            glUniformMatrix4fv(m_uniforms.Projection, 1, 0, projectionMatrix.Pointer()); // replace
           

            // Set the color.
//            vec3 color = visual->Color;
//            glVertexAttrib4f(m_colorSlot, color.x, color.y, color.z, 1);
//           
            // Set the diffuse color.  // new and replace
            vec3 color = visual->Color * 0.75f;
            glVertexAttrib4f(m_attributes.DiffuseMaterial, color.x, color.y, color.z, 1);
           
            // Draw the wireframe.
            //int stride = sizeof(vec3);
            //const Drawable& drawable = m_drawables[visualIndex];
            //glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
            //glVertexAttribPointer(m_positionSlot, 3, GL_FLOAT, GL_FALSE, stride, 0);
            //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
            //glDrawElements(GL_LINES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
           
           
            // Draw the surface.  // new and replace
            int stride = 2 * sizeof(vec3);                          // replace
            const GLvoid* offset = (const GLvoid*) sizeof(vec3);    // new
            GLint position = m_attributes.Position;                 // new
            GLint normal = m_attributes.Normal;                     // new

            const Drawable& drawable = m_drawables[visualIndex];
            glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
            glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);             // replace
           
            glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset);          // new

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
           
            glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);       // replace , use triangles
           
        }
    }
   
    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]);
            std::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]);
            std::cout << messages;
            exit(1);
        }
         return programHandle;
    }
     
    void RenderingEngine::setLightPos(float x, float y, float z)  // const // 使用const就變成惟讀
    {
        lightPosX = x;
        lightPosY = y;
        lightPosZ = z;
    }

}

10. 結果,分別為啓動原始圖、調整光照的位置及轉動小圖中物件的方向






PS:在此表面運算使用三角運算取代線條








void ParametricSurface::GenerateLineIndices(vector<unsigned short>& indices) const
{
    indices.resize(GetLineIndexCount());
    vector<unsigned short>::iterator index = indices.begin();
    for (int j = 0, vertex = 0; j < m_slices.y; j++) {
        for (int i = 0; i < m_slices.x; i++) {
            int next = (i + 1) % m_divisions.x;
            *index++ = vertex + i;
            *index++ = vertex + next;
            *index++ = vertex + i;
            *index++ = vertex + i + m_divisions.x;
        }
        vertex += m_divisions.x;
    }
}

void
ParametricSurface::GenerateTriangleIndices(vector<unsigned short>& indices) const
{
    indices.resize(GetTriangleIndexCount());
    vector<unsigned short>::iterator index = indices.begin();
    for (int j = 0, vertex = 0; j < m_slices.y; j++) {
        for (int i = 0; i < m_slices.x; i++) {  // 此處要看上圖所繪,兩個三角形的點,故有六個
            int next = (i + 1) % m_divisions.x;
            *index++ = vertex + i;
            *index++ = vertex + next;
//重複 
            *index++ = vertex + i + m_divisions.x;  //重複
            *index++ = vertex + next;  
//重複 
            *index++ = vertex + next + m_divisions.x;
            *index++ = vertex + i + m_divisions.x; 
//重複
        }
        vertex += m_divisions.x;
    }
}