Cg_OpenGL Lighting

光照模型將採用廣泛應用的phong模型,雖然這種模型在openGL的固定管線中已經實現了,但是學習該光照模型可以更加清楚的瞭解可編程渲染管線的流程。

 

首先要實現phong光照模型先要了解該模型中光照計算,關於phong模型光照計算的相關資料網上相當多,或者參考任何一本計算機圖形學的相關書籍即可,這裏只給出計算公式。

 

C = ambient + diffuse + specular

 

從公式中可以得出,物體頂點的最終顏色是由環境反射,漫反射和鏡面反射三個成分來決定的。環境反射的計算公式爲:

 

ambient = IaKa

 

其中,Ia是光的環境反射強度,Ka是物體材質的環境射係數。環境反射很簡單,和光源、法線等等都沒有關係,下面是隻有環境反射的例子。

 

 

Fig1環境反射,Ia=(1.0, 1.0, 1.0)Ka=(0.15,0.15,0.0)

 

和環境反射相比,漫反射就稍微複雜一點,它和光源的位置和物體頂點的法線都有關係。漫反射的計算公式爲:

 

diffuse = I­d­K(NL)

 

其中I­d­是光的漫反射強度,Kd是物體材質漫反射係數,NL表示法線N和入射光線L的內積。下面是隻有面反射的例子。

 

 

Fig2漫反射,Id=(1.0, 1.0, 1.0)Kd=(1.0, 1.0 , 0.0)

 

最後是鏡面反射,和漫反射相比,鏡面反射又複雜了一點。它不僅和光源位置,物體頂點法線有關係,而且還和我們觀看的位置有關係。鏡面反射的計算公式爲:

 

specular = IsKs(VR)­n

 

其中Is是光的鏡面反射強度,Ks是物體材質的鏡面反射係數,VR表示相機朝向向量V和反射光線R的內積,n表示該內積的n次冪。這裏反射光線R可以通過公式

 

= 2(LN)N-L­

 

來得到。這裏還可以使用half vector來計算,計算half vector要比計算反射向量R方便快速的多。

 

H=(L+V)/2

 

所以,鏡面反射公式現在可以寫成

 

specular = IsKs(NH)­n

 

下面是隻有鏡面反射的例子。

 

 

 

Fig3鏡面反射,Is=(1.0, 1.0, 1.0)Ks=(1.0, 1.0 , 1.0,), n=32

通過上面的過程,分別計算出了物體每個頂點的環境反射,面反射和鏡面反射。最後簡單將這三個成分相加即可得到頂點最終的顏色。

 

C = IaKa + I­d­K(NL)IsKs(NH)­n

 

圖fig4顯示了這個相加的過程。

 

+

+

=

Fig4 phong光照

 

下面是vertex shader的代碼。由於是基於vertex的光照,所以不需要fragment shader。

vertex腳本:

uniform float3 LightPosition; //光源位置
uniform float3 eyePosition;   //相機位置
uniform float3 I;              //光強度
uniform float3 Ka;             //環境光反射係數
uniform float3 Kd;             //漫反射係數
uniform float3 Ks;             //鏡面反射係數
uniform float shininess;      //n冪次
 
struct output
{
      float4 position : POSITION; 
      float4 color     : COLOR; 
};
 
output v_main( float4 position : POSITION,
                     float3 normal   : NORMAL,
                     uniform float4x4 MV, // 在相機座標系中計算,所以要用到ModelView變換矩陣
                     uniform float4x4 MVP // ModelViewProjection變換矩陣
                    )
{
      output OUT;
      OUT.position = mul(MVP, position);
 
      float3 N = normalize(mul(MV, float4(normal,0.0)) ).xyz; //轉換法線到相機座標系
      float3 P = mul(MV, position).xyz; //轉換物體頂點到相機座標系
 
      float3 L = normalize(LightPosition - P);
      float NdotL = max(dot(N,L),0); //判斷法線和入射光線的角度是否大於90度
 
      float3 ambient = Ka * I; //環境反射
      float3 diffuse = Kd * I * NdotL; //漫反射
 
      float3 V = normalize(eyePosition - P);
      float3 H = normalize(L+V); //half vector
      float NdotH = pow(max(dot(N,H), 0), shininess);
      if(NdotL<=0) NdotH = 0.0;
 
      float3 specular = Ks*I*NdotH; //鏡面反射
 
      float3 color = ambient + diffuse +specular; //所有成分相加
      OUT.color.xyz= color;
      OUT.color.w = 1.0;
 
      return OUT;
}


主程序:

#include <gl/glut.h>
#include <cg/cg.h>
#include <Cg/cgGL.h>
#include <stdio.h>
#include <Windows.h>

int ww = 640, hh = 480;

void render();
void reshape(int w, int h);

static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile;
static CGprogram   myCgVertexProgram;

static const char *myProgramName = "Lighting CG";
static const char *myVertexProgramFileName = "CSDN_02v.cg";
static const char *myVertexProgramName = "v_main";


CGparameter lp, ep, i, a, d, s, n;
//display FPS on the title of the window
static float lastTime   = 0.0f;
void displayFPS(){
	static float framesPerSecond    = 0.0f;       // This will store our fps
	// This will hold the time from the last frame
	float currentTime = GetTickCount() * 0.001f;    
	if( currentTime - lastTime > 0.0f )
	{
		framesPerSecond = 1/(currentTime - lastTime);
		char strFrameRate[256];
		lastTime = currentTime;
		sprintf(strFrameRate, "Current Frames Per Second: %f", framesPerSecond);
		glutSetWindowTitle( strFrameRate);
		framesPerSecond = 0;
	}
}


int main(int argc, char** argv)
{
	//【1】初始化部分
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(ww,hh);
	glutCreateWindow(myProgramName);

	//【2】構建shader運行環境;
	myCgContext = cgCreateContext();
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);

	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);

	myCgVertexProgram =
		cgCreateProgramFromFile(
		myCgContext,              /* Cg runtime context */
		CG_SOURCE,                /* Program in human-readable form */
		myVertexProgramFileName,  /* Name of file containing program */
		myCgVertexProfile,        /* Profile: OpenGL ARB vertex program */
		myVertexProgramName,      /* Entry function name */
		NULL);                    /* No extra compiler options */

	cgGLLoadProgram(myCgVertexProgram);


	//【3】將shader載入,並運行
	glutDisplayFunc(render);
	glutReshapeFunc(reshape);

	glEnable(GL_DEPTH_TEST);



	glutMainLoop();
	return 0;
}

void reshape(int w, int h)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45, (float)w/(float)h, 0.1, 100);
	glViewport(0,0,w,h);

	ww = w;
	hh = h;
}

void render()
{
	displayFPS();
	//【3.1】gl視圖變換
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(.0f, .0f, .2f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(.0,.0,5.0, .0,.0,.0, .0,1.0,.0);

	static float angle;
	glRotatef(angle, 0.0,1.0,0.0);

	//【3.2】把程序與當前API狀態綁定起來;
	cgGLBindProgram(myCgVertexProgram);
	cgGLEnableProfile(myCgVertexProfile);

	//將ModelViewProjection矩陣傳入shader
	CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
	cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
	
	CGparameter mv = cgGetNamedParameter(myCgVertexProgram, "MV");
	cgGLSetStateMatrixParameter(mv, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
	//注意傳參的方法☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
	
	lp = cgGetNamedParameter(myCgVertexProgram, "LightPosition");
	ep = cgGetNamedParameter(myCgVertexProgram, "eyePosition");
	i = cgGetNamedParameter(myCgVertexProgram, "I");
	a = cgGetNamedParameter(myCgVertexProgram, "Ka");
	d = cgGetNamedParameter(myCgVertexProgram, "Kd");
	s = cgGetNamedParameter(myCgVertexProgram, "Ks");
	n = cgGetNamedParameter(myCgVertexProgram, "shininess");

	cgSetParameter3f(lp,1.0,1.0,1.0);
	cgSetParameter3f(ep,0,0.5,0);
	cgSetParameter3f(i ,1.0, 1.0, 1.0);
	cgSetParameter3f(a ,0.15,0.15, 0.0);
	cgSetParameter3f(d ,1.0, 1.0 , 0.0);
	cgSetParameter3f(s ,1.0, 1.0 , 1.0);
	cgSetParameter1f(n ,32);


	glutSolidTorus(0.3,1.0,30,30);

	cgGLDisableProfile(myCgVertexProfile);

	angle += 0.5;
	if(angle >=360) angle = 0.0f;

	glutSwapBuffers();
	glutPostRedisplay();
}



由於是基於vertex的光照,雖然採用gouraud shading要比flat shading效果好的多,但是和phong shading的效果相差很大。這裏大家要注意的是,phong modelphong shading的區別。上一篇教程所講的光照模型叫phong model,而這篇教程要介紹的一種着色方法叫phong shading,必須要使用shader才能實現。下面的圖中對比了採用flat shadinggouraud shadingphong shading技術渲染的一個圓環。

  

Flat shading

Gouraud shading

Phong shading


Cg來實現基於pixel lighingphong shading光照就容易多了。絕大部分的Cg shader代碼都是一樣的,主要的改變就是這次不是在vertex shader裏光照,而是在fragment shader裏計算光照。所以整個vertex shader的代碼很簡單,將要渲染的物體的頂點位置,頂點法線傳入fragment shader就是vertex shader的全部工作。整個vertex shader的代碼如下。

  

上面的代碼的輸出結構體中有三個成員。Position是傳入的物體的頂點,該頂點將用ModelViewProjection矩陣轉換成剪裁座標系中的座標供光柵化使用。一旦將座標轉換後,我們就無法在fragment shader中使用物體的頂點座標了。由於要在fragmentshader中計算光照,所以我們要將物體的頂點位置,頂點法線都傳入到fragment shader中。這裏物體轉換前的頂點和法線分別使用了語義TEXCOORD0TEXCOORD1,代表將它們作爲紋理座標後傳入fragment shader。這樣GPU會把頂點和法線信息當做紋理座標來處理,在貼圖的時候,GPU會根據紋理自動插值計算每個像素對應的顏色,而現在GPU插值計算出來的就是每個像素對應的座標和法線信息了。有了這些數據就可以進行phong shading的計算了。光照計算的公式和方法和上一個教程介紹的一模一樣。

vertex腳本:

struct output
{
      float4 position  : POSITION;    
      float3 objectPos : TEXCOORD0;   
      float3 normal     : TEXCOORD1;
};
 
output v_main( float4 position : POSITION,
                  float3 normal   : NORMAL,
                  uniform float4x4 MV,
                  uniform float4x4 MVP
                    )
{
      output OUT;
      OUT.position = mul(MVP, position);
      OUT.objectPos = mul(MV, position).xyz;
      OUT.normal = mul(MV, float4(normal,0.0)).xyz;
 
      return OUT;
}


fragment腳本:

uniform float3 LightPosition;
uniform float3 eyePosition;
uniform float3 I;
uniform float3 Ka;
uniform float3 Kd;
uniform float3 Ks;
uniform float shininess;
 
struct input{
      float3 objectPos: TEXCOORD0;   
      float3 normal   : TEXCOORD1;
};
 
struct output{
      float4 color     : COLOR;
};
 
output f_main( in input IN )
{
      output OUT;
 
      float3 N = normalize(IN.normal);
      float3 P = IN.objectPos;
 
      float3 L = normalize(LightPosition - P);
      float NdotL = max(dot(N,L),0);
 
      float3 ambient = Ka * I;
      float3 diffuse = Kd * I * NdotL;
 
      float3 V = normalize(eyePosition - P);
      float3 H = normalize(L+V);
      float NdotH = pow(max(dot(N,H), 0), shininess);
 
      if(NdotL<=0)
           NdotH = 0.0;
      float3 specular = Ks*I*NdotH;
 
      float3 color = ambient + diffuse + specular;
      OUT.color.xyz= color;
      OUT.color.w = 1.0;
 
      return OUT;
}


主程序:

#include <gl/glut.h>
#include <cg/cg.h>
#include <Cg/cgGL.h>
#include <stdio.h>
#include <Windows.h>


int ww = 640, hh = 480;


void render();
void reshape(int w, int h);


static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile;
static CGprofile   myCgFragmentProfile;
static CGprogram   myCgVertexProgram;
static CGprogram   myCgFragmentProgram;


static const char *myProgramName = "Lighting CG2";
static const char *myVertexProgramFileName = "CSDN_03v.cg";
static const char *myVertexProgramName = "v_main";
static const char *myFragmentProgramFileName = "CSDN_03f.cg";
static const char *myFragmentProgramName = "f_main";


CGparameter lp, ep, i, a, d, s, n;
//display FPS on the title of the window




void displayFPS(){
	static float lastTime   = 0.0f;
	static float framesPerSecond    = 0.0f;       // This will store our fps
	// This will hold the time from the last frame
	float currentTime = GetTickCount() * 0.001f;    
	if( currentTime - lastTime > 0.0f )
	{
		framesPerSecond = 1/(currentTime - lastTime);
		char strFrameRate[256];
		lastTime = currentTime;
		sprintf(strFrameRate, "Current Frames Per Second: %f", framesPerSecond);
		glutSetWindowTitle( strFrameRate);
		framesPerSecond = 0;
	}
}




int main(int argc, char** argv)
{
	//【1】初始化部分
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(ww,hh);
	glutCreateWindow(myProgramName);


	//【2】構建shader運行環境;
	myCgContext = cgCreateContext();
	cgGLSetDebugMode(CG_FALSE);
	cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);


	myCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(myCgVertexProfile);


	myCgVertexProgram =
		cgCreateProgramFromFile(
		myCgContext,              /* Cg runtime context */
		CG_SOURCE,                /* Program in human-readable form */
		myVertexProgramFileName,  /* Name of file containing program */
		myCgVertexProfile,        /* Profile: OpenGL ARB vertex program */
		myVertexProgramName,      /* Entry function name */
		NULL);                    /* No extra compiler options */
	cgGLLoadProgram(myCgVertexProgram);


	myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(myCgFragmentProfile);


	myCgFragmentProgram =
		cgCreateProgramFromFile(
		myCgContext,                /* Cg runtime context */
		CG_SOURCE,                  /* Program in human-readable form */
		myFragmentProgramFileName,  /* Name of file containing program */
		myCgFragmentProfile,        /* Profile: OpenGL ARB vertex program */
		myFragmentProgramName,      /* Entry function name */
		NULL);                      /* No extra compiler options */
	cgGLLoadProgram(myCgFragmentProgram);
	
	


	//【3】將shader載入,並運行
	glutDisplayFunc(render);
	glutReshapeFunc(reshape);


	glEnable(GL_DEPTH_TEST);






	glutMainLoop();
	return 0;
}


void reshape(int w, int h)
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();


	gluPerspective(45, (float)w/(float)h, 0.1, 100);
	glViewport(0,0,w,h);


	ww = w;
	hh = h;
}


void render()
{
	displayFPS();
	//【3.1】gl視圖變換
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(.0f, .0f, .2f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(.0,.0,5.0, .0,.0,.0, .0,1.0,.0);


	static float angle;
	glRotatef(angle, 0.0,1.0,0.0);


	//【3.2】把程序與當前API狀態綁定起來;
	cgGLBindProgram(myCgVertexProgram);
	cgGLEnableProfile(myCgVertexProfile);
	cgGLBindProgram(myCgFragmentProgram);
	cgGLEnableProfile(myCgFragmentProfile);


	//將ModelViewProjection矩陣傳入shader
	CGparameter mvp = cgGetNamedParameter(myCgVertexProgram, "MVP");
	cgGLSetStateMatrixParameter(mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
	
	CGparameter mv = cgGetNamedParameter(myCgVertexProgram, "MV");
	cgGLSetStateMatrixParameter(mv, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
	//傳參的方法


	lp = cgGetNamedParameter(myCgFragmentProgram, "LightPosition");
	ep = cgGetNamedParameter(myCgFragmentProgram, "eyePosition");
	i = cgGetNamedParameter(myCgFragmentProgram, "I");
	a = cgGetNamedParameter(myCgFragmentProgram, "Ka");
	d = cgGetNamedParameter(myCgFragmentProgram, "Kd");
	s = cgGetNamedParameter(myCgFragmentProgram, "Ks");
	n = cgGetNamedParameter(myCgFragmentProgram, "shininess");


	cgSetParameter3f(lp,1.0,1.0,1.0);
	cgSetParameter3f(ep,0,0.5,0);
	cgSetParameter3f(i ,1.0, 1.0, 1.0);
	cgSetParameter3f(a ,0.15,0.15, 0.0);
	cgSetParameter3f(d ,1.0, 1.0 , 0.0);
	cgSetParameter3f(s ,1.0, 1.0 , 1.0);
	cgSetParameter1f(n ,32);




	glutSolidTorus(0.3,1.0,30,30);


	cgGLDisableProfile(myCgVertexProfile);
	cgGLDisableProfile(myCgFragmentProfile);


	angle += 0.5;
	if(angle >=360) angle = 0.0f;


	glutSwapBuffers();
	glutPostRedisplay();
}


phong model結果1:                                                                                                    phong shading結果2:

                                             


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章