代码下载:http://download.csdn.net/download/dango_miracle/10247591
由于一开始没学明白,所以老师说要自己尝试使用着色器的时候我当然是四处复制程序尝试把它们运行起来。然而复制粘贴来一堆东西以后还是运行不了,窗口一片漆黑……非常痛苦了。如果有跟我一样刚开始学习着色器并且一脸懵逼的小伙伴看到这篇博客,希望这里提供的代码能给你提供一些温暖 (。・∀・)ノ゛
正如前一篇博文中提到,着色器是用来取代固定的图形渲染管线的。那要如何取代呢?当然是有接口的!于是你上网搜索,发现别人总是只给出了着色器的代码,却没有告诉你要怎么使用。看老师上课好像是把着色器存成txt就可以用了,于是你也照猫画虎……然而什么都没有啊!不是报错就是窗口一片漆黑啊!
↑ 是我。
当然不可能是把着色器的代码存成了文本然后扔在文件夹里就能用了。要想使用着色器,你至少得先做以下几件事:
1. 让你的主程序找到着色器代码(文件读取)
2. 让你的主程序能理解着色器代码(编译?我也不知道要怎么形容)
3. 告诉你的程序要使用哪个着色器(绑定着色器)
以下是一个示例主程序与相应的着色器,着色器的代码用的是gooch shader(暂时不用理解):
//myshader.cpp
#include<gl\glew.h>
#include<gl\glut.h>
#include<iostream>
#include<fstream>
using namespace std;
#pragma comment(lib,"glew32.lib")
GLuint vertexshader;
GLuint fragmentshader;
GLuint vaoHandle;
GLuint myshader;
char *shaderLoadSource(char *fn);
void InitShader();
void display();
void reshape(int w, int h);
//读取着色器文件
char *shaderLoadSource(char *fn) {
FILE *fp;
char *content = NULL;
int count = 0;
if (fn != NULL) {
fp = fopen(fn, "rt");
if (fp != NULL) {
fseek(fp, 0, SEEK_END);
count = ftell(fp);
rewind(fp);
if (count > 0) {
content = (char *)malloc(sizeof(char) * (count + 1));
count = fread(content, sizeof(char), count, fp);
content[count] = '\0';
}
fclose(fp);
}
}
return content;
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
glEnable(GL_DEPTH_TEST);
glUseProgram(myshader);
glutSolidTeapot(1);
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.0, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
//初始化着色器
void InitShader() {
char* vershadercode = NULL;
char* frgshadercode = NULL;
vertexshader = glCreateShader(GL_VERTEX_SHADER);
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
vershadercode = shaderLoadSource("vertex.txt");
frgshadercode = shaderLoadSource("fragment.txt");
glShaderSource(vertexshader, 1, &vershadercode, NULL);
glCompileShader(vertexshader);
glShaderSource(fragmentshader, 1, &frgshadercode, NULL);
glCompileShader(fragmentshader);
myshader = glCreateProgram();
glAttachShader(myshader, vertexshader);
glAttachShader(myshader, fragmentshader);
glLinkProgram(myshader);
}
int main(int argc, char**argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("My first shader!");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glClearColor(0.0, 0.0, 0.0, 0.0);
glewInit();
InitShader();
glutMainLoop();
return 0;
}
//vertex.txt
varying float NdotL;
varying vec3 ReflectVec;
varying vec3 ViewVec;
void main () {
vec3 ecPos = vec3(gl_ModelViewMatrix* gl_Vertex);
vec3 tnorm =normalize(gl_NormalMatrix *gl_Normal);
vec3 lightVec =normalize(gl_LightSource[0].position.xyz - ecPos);
ReflectVec = normalize(reflect(-lightVec, tnorm));
ViewVec = normalize(-ecPos);
NdotL = (dot(lightVec, tnorm) +1.0) * 0.5;
gl_Position = ftransform();
gl_FrontColor = vec4(vec3(0.75), 1.0);
gl_BackColor = vec4(0.0);
}
//fragment.txt
vec3 SurfaceColor = vec3(0.75, 0.75, 0.75);
vec3 WarmColor = vec3(0.1, 0.4, 0.8);
vec3 CoolColor = vec3(0.6, 0.0, 0.0);
float DiffuseWarm = 0.45;
float DiffuseCool = 0.045;
varying float NdotL;
varying vec3 ReflectVec;
varying vec3 ViewVec;
void main() {
vec3 kcool = min(CoolColor + DiffuseCool* vec3(gl_Color), 1.0);
vec3 kwarm = min(WarmColor + DiffuseWarm* vec3(gl_Color), 1.0);
vec3 kfinal = mix(kcool, kwarm, NdotL) *gl_Color.a;
vec3 nreflect = normalize(ReflectVec);
vec3 nview = normalize(ViewVec);
float spec = max(dot(nreflect, nview),0.0);
spec = pow(spec, 32.0);
gl_FragColor = vec4(min(kfinal + spec, 1.0),1.0);
}
只要将这三个文件放在同一个文件夹下即可,运行后应当能看到如下的窗口:
这样,我们就成功运行了第一个着色器程序,撒花!
注释:
1. vertex.txt 和 fragment.txt 文件的后缀名是无所谓的,是什么都可以,为了方便辨别着色器类别很多人会给后缀取名叫vert和frag,反正二进制文件读取起来都是一样的
2. 这只是一个最普通的主程序里,里面的操作只是画了一个茶壶。修改着色器代码即可更改茶壶的显示效果,如改变颜色,光照等。这部分内容就是网上提供的那些着色器代码啦
3. 为什么用了网上的着色器代码就显示不了东西了?一般首先就看着色器中有没有用到主程序中传入的参数(uniform型的变量)。如果有,但你又没有真的传入,当然显示不了啦