OpenGL三维几何变换 & 材质光照
显示效果:
源码:
#define NDEBUG
#ifndef GLUT_DISABLE_ATEXIT_HACK
#define GLUT_DISABLE_ATEXIT_HACK
#endif
#include <windows.h>
#include <gl/glut.h>
#include <math.h>
#include <stdio.h>
#include <vector>
using namespace std;
const int windowWidge=600, windowHeight=600;
static GLfloat angle = 0.0f;
static GLfloat moonAngle=0.0f;
static GLfloat sunAngle=0.0f;
//初始化绘制
void init(){
glClearColor(0.0,0.0,0.0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清理颜色和深度缓存
// 创建透视效果视图
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0, 1.0f, 1.0f, 50.0f);
//定义视点位置:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 30.0, 10.0, 0.0, 0.0, 0.0, 0.0, -1, 3.0);
// 定义太阳光源,它是一种白色的光源
{
GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f}; //光源的位置在世界坐标系圆心,齐次坐标形式
GLfloat sun_light_ambient[] = {0.1f, 0.1f, 0.1f, 1.0f}; //RGBA模式的环境光
GLfloat sun_light_diffuse[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //RGBA模式的漫反射光,全白光
GLfloat sun_light_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //RGBA模式下的镜面光 ,全白光
glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);
//开启灯光
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
return ;
}
void display(){
glClearColor(0.0,0.0,0.0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 定义太阳的材质并绘制太阳
{
GLfloat sun_mat_ambient[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //定义材质的环境光颜色
GLfloat sun_mat_diffuse[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的漫反射光颜色
GLfloat sun_mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的镜面反射光颜色
GLfloat sun_mat_emission[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //定义材质的辐射光颜色
GLfloat sun_mat_shininess = 0.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, sun_mat_shininess);
glPushMatrix ();
glRotated (sunAngle,0.0f, 0.0f, 1.0f);
glutSolidSphere(4.0, 40, 32);
glPopMatrix ();
}
// 定义地球的材质并绘制地球
{
GLfloat earth_mat_ambient[] = {0.0f, 0.0f, 1.0f, 1.0f}; //定义材质的环境光颜色
GLfloat earth_mat_diffuse[] = {0.0f, 0.0f, 1.0f, 1.0f}; //定义材质的漫反射光颜色
GLfloat earth_mat_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //定义材质的镜面反射光颜色
GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的辐射光颜色
GLfloat earth_mat_shininess = 32.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, earth_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, earth_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, earth_mat_shininess);
glPushMatrix ();
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glTranslatef(10.0f, 0.0f, 0.0f);
glutSolidSphere(2.0, 40, 32); //半径为2, 40条纬线, 32条经线
glPopMatrix ();
}
// 定义月球材质并绘制月球
{
GLfloat moon_mat_ambient[] = {225.0/255, 225.0/255, 220.0/255, 1.0f}; //定义材质的环境光颜色
GLfloat moon_mat_diffuse[] = {225.0/255, 225.0/255, 220.0/255, 1.0f}; //定义材质的漫反射光颜色
GLfloat moon_mat_specular[] = {1.0f, 234.0/255, 106.0/255, 1.0f}; //定义材质的镜面反射光颜色
GLfloat moon_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的辐射光颜色
GLfloat moon_mat_shininess = 128.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, moon_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, moon_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, moon_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, moon_mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, moon_mat_shininess);
//绕地球旋转:
glPushMatrix ();
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glTranslatef(10.0f, 0.0f, 0.0f);
glRotatef(-angle, 0.0f, 0.0f, 1.0f);
glRotatef (moonAngle,0.0f, 0.0f, 1.0f);
glTranslatef (4.0f, 0.0f, 0.0f);
glutSolidSphere(1.0, 40, 32); //半径为2, 40条纬线, 32条经线
glPopMatrix ();
}
glutSwapBuffers();
return ;
}
void myIdle(void){
angle += 0.5f;
moonAngle +=2.0f;
sunAngle +=1.0f;
if( angle >= 360.0f ){
angle = 0.0f;
}
if(moonAngle >=360.0f){
moonAngle=0.0f;
}
glutPostRedisplay();
Sleep(10);
return;
}
int main(int argc, char** argv){
glutInit(&argc, argv);//初始化glut
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);//设置显示模式为单缓冲,RGB模式
glutInitWindowPosition(0,0);//设置窗口位置
glutInitWindowSize(windowWidge,windowHeight);//设置窗口大小
glutCreateWindow("太阳系");//设置窗口标题
init();
glutDisplayFunc(display);
glutIdleFunc(myIdle);
glutMainLoop();
return 0;
}
源码分析:
视点透视:
// 创建透视效果视图
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0, 1.0f, 1.0f, 50.0f);
-
先搞明白glMatrixMode()
glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标
GL_PROJECTION是对投影矩阵操作
GL_MODELVIEW是对模型视景矩阵操作
GL_TEXTURE是对纹理矩阵进行随后的操作 -
之前讲到openGL有两种投影: 正射投影(垂直投影)和透视投影
前者不用缩放, 后者使用缩放
-
通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。
所以后头会跟上一个:glLoadIdentity();
-
gluPerspective(80.0, 1.0f, 1.0f, 50.0f);
设置当前可视空间为透视投影空间, 并设置透视投影的参数
之前用的都是glOrtho
此为设置当前可视空间为正投影空间void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
几个参数与图中的视角相对应:
-
视线张角
当张角变小时, 相当于显微镜, 将更小范围内的东西放大到固定的屏幕, 反之缩小
当=180时, 物体变的无限小, 不可见, 当=0时, 视线关闭, 不可见
-
宽高比w/h
-
zNear表示近裁剪面到眼睛的距离,zFar表示远裁剪面到眼睛的距离
这两个都不能设置为负值
-
定义视点:
//定义视点:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 30.0, 10.0, 0.0, 0.0, 0.0, 0.0, -1, 3.0);
-
前两个和之前一样
-
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez, GLdouble centerx,GLdouble centery,GLdouble centerz, GLdouble upx,GLdouble upy,GLdouble upz);
定义视点, 分为三组坐标
- 第一组定义了视点在世界坐标系中的坐标
- 第二组定义了视点看向的位置
- 第三组定义了视点向上方向的在世界坐标系中的方向向量(头顶位置)
光照:
具体的光照详解可以参考这一篇:
https://blog.csdn.net/chuifuhuo6864/article/details/100886020
三维变换:
三维变换具体可以参考这篇博客:
https://www.cnblogs.com/tjulym/p/5049313.html
glPushMatrix ();
glRotatef(angle, 0.0f, 0.0f, 1.0f);
glTranslatef(10.0f, 0.0f, 0.0f);
glutSolidSphere(2.0, 40, 32); //半径为2, 40条纬线, 32条经线
glPopMatrix ();
- 矩阵入栈, 而后对此矩阵进行操作
以下的操作都可以看做是对当前坐标轴的变换, 初始坐标轴与世界坐标轴重合
-
旋转:
glRotatef(GLfloat angle,GLfloat x,GLfloat y,GLfloat z)
-
angle:
旋转的角度 -
后头一组坐标为旋转轴的方向向量
将以此向量为上进行逆时针旋转
-
-
平移:
void glTranslatef(GLfloat x,GLfloat y,GLfloat z);
以当前坐标轴为基准, 将当前坐标轴平移到(x,y,z)的位置
-
画一个刚体球:
void glutSolidSphere(GLdouble r,GLint ,GLint);
- r
球的半径 - 后头两个参数为球的纬线&经线的条数
实际画出的是一个多面体, 所以纬线与经线越多, 就越接近真实球体
- r
-
弹出当前操作的矩阵
矩阵堆栈有容量上限, 及时弹出防止爆栈
画面刷新:
glutIdleFunc(myIdle);
void myIdle(void){
angle += 0.5f;
moonAngle +=2.0f;
sunAngle +=1.0f;
if( angle >= 360.0f ){
angle = 0.0f;
}
if(moonAngle >=360.0f){
moonAngle=0.0f;
}
sleep(10);
glutPostRedisplay();
return;
}
-
glutIdleFunc(void (*callback) ())
闲时调用函数, 当openGL空闲时会调用传给他的函数func
-
myIdle()
返回值与参数均为void
在空闲时被调用, 更新当前角度啥的
而后一个sleep() 防止刷新率过高
-
glutPostRedisplay()
相当于直接调用display()来刷新窗口, 但后者更加智能一点