在計算機圖形學中,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看起來真實感較佳。




沒有留言:
張貼留言