OpenGL 蓝书学习(第四版)第六章
OpenGL 蓝书学习(第四版)第六章
本章是颜色混合相关的内容
球体世界圆环倒影
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <numbers>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <cmath>
#include <iostream>
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLfloat ambientLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
static constexpr GLfloat diffuseLight[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -100.0F, 100.0F, 50.0F, 1.0F };
static constexpr GLfloat lightPosMirror[] = { -100.0F, -100.0F, 50.0F, 1.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(35.0F, aspectRatio, 1.0F, 50.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
break;
case GLUT_KEY_DOWN:
break;
case GLUT_KEY_LEFT:
break;
case GLUT_KEY_RIGHT:
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void drawGround(){
constexpr GLfloat y = 0.0F;
constexpr GLfloat edge = 20.0F;
constexpr GLfloat step = 0.5F;
GLint count = 0;
glShadeModel(GL_FLAT);
for (GLfloat i = -edge, color = 0.0F; i <= edge; i += step) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0F, 1.0F, 0.0F); // All Point up
for (GLfloat j = edge; j >= -edge; j -= step) {
color = (count % 2 == 0) ? 1.0F : 0.0F;
glColor4f(color, color, color, 0.5F);
glVertex3f(i, y, j);
glVertex3f(i + step, y, j);
count ++ ;
}
glEnd();
}
glShadeModel(GL_SMOOTH);
}
void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const GLint numMajor, const GLint numMinor) {
glPushMatrix();
glTranslatef(0.0F, 0.1F, -2.5F);
for (GLint i = 0; i < numMajor; ++i) {
const GLfloat u0 = (GLfloat)i / numMajor * 2.0F * PI;
const GLfloat u1 = (GLfloat)(i + 1) / numMajor * 2.0F * PI;
glBegin(GL_TRIANGLE_STRIP);
for (GLint j = 0; j <= numMinor; ++j) {
GLfloat v = (GLfloat)j / numMinor * 2.0F * PI;
GLfloat cosV = cosf(v), sinV = sinf(v);
// 点1 (u0 经线)
GLfloat cu0 = cosf(u0), su0 = sinf(u0);
glNormal3f(cu0 * cosV, su0 * cosV, sinV);
glVertex3f(cu0 * (majorRadius + minorRadius * cosV),
su0 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
// 点2 (u1 经线)
GLfloat cu1 = cosf(u1), su1 = sinf(u1);
glNormal3f(cu1 * cosV, su1 * cosV, sinV);
glVertex3f(cu1 * (majorRadius + minorRadius * cosV),
su1 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
}
glEnd();
}
glPopMatrix();
}
void drawWorld(){
glColor3f(1.0F, 0.0F, 0.0F);
glPushMatrix();
glTranslatef(0.0F, 0.5F, -3.5F);
glPushMatrix();
// glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
glTranslatef(1.0F, 0.0F, 0.0F);
glutSolidSphere(0.1F, 17, 9);
glPopMatrix();
drawTorus(0.35F, 0.15F, 40, 20);
glPopMatrix();
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLightfv(GL_LIGHT0, GL_POSITION, lightPosMirror);
glTranslatef(0.0F, -0.4F, 0.0F);
glPushMatrix();
glFrontFace(GL_CW); // 绕 Y 轴镜像,顺时针为正面
glScalef(1.0F, -1.0F, 1.0F); // XZ 平面 镜像
drawWorld();
glFrontFace(GL_CCW); // 恢复默认
glPopMatrix();
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawGround();
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
drawWorld();
glPopMatrix();
glutSwapBuffers();
}
void setupRc(){
constexpr GLfloat fNoLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
constexpr GLfloat fLowLight[] = { 0.25F, 0.25F, 0.25F, 1.0F };
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.25F, 0.25F, 0.25F, 1.0F);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}
混合抗锯齿
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <numbers>
#include <cmath>
#include <iostream>
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
// static GLfloat xRot = 0.0F; // x旋转角度
// static GLfloat yRot = 0.0F; // y旋转角度
// static GLfloat zRot = 0.0F; // z旋转角度
static GLint mainWindowId = 0; // 主窗口ID
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// gluOrtho2D 属于 GLU 库,Windows/Linux 上需额外链接 glu32/libGLU;
// 改用核心函数 glOrtho(gluOrtho2D(l,r,b,t) 等价于 glOrtho(l,r,b,t,-1.0,1.0)),
// 即可消除 GLU 依赖,跨平台通用。
glOrtho(0.0, static_cast<GLdouble>(width), 0.0, static_cast<GLdouble>(height), -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
break;
case 'd':
case 'D':
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
break;
case GLUT_KEY_DOWN:
break;
case GLUT_KEY_LEFT:
break;
case GLUT_KEY_RIGHT:
break;
default:
return; // 不处理其他按键
}
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 绘制moon (月亮)
glBegin(GL_TRIANGLE_FAN);
glVertex2f(400.0F, 300.0F); // 月亮中心
constexpr GLfloat radius = 50.0F;
constexpr GLint numSegments = 100;
for (GLint i = 0; i <= numSegments; ++i) {
const GLfloat angle = 2.0F * PI * static_cast<GLfloat>(i) / static_cast<GLfloat>(numSegments);
glVertex2f(400.0F + radius * cosf(angle), 300.0F + radius * sinf(angle));
}
glEnd();
// 绘制star
glPointSize(5.5F);
glBegin(GL_POINTS);
glVertex2f(100.0F, 500.0F);
glVertex2f(150.0F, 550.0F);
glVertex2f(200.0F, 400.0F);
glVertex2f(250.0F, 500.0F);
glVertex2f(300.0F, 450.0F);
glVertex2f(350.0F, 500.0F);
glVertex2f(500.0F, 550.0F);
glVertex2f(550.0F, 500.0F);
glVertex2f(400.0F, 500.0F);
glVertex2f(450.0F, 500.0F);
glVertex2f(600.0F, 350.0F);
glVertex2f(650.0F, 400.0F);
glVertex2f(700.0F, 500.0F);
glVertex2f(750.0F, 450.0F);
glVertex2f(800.0F, 400.0F);
glEnd();
// Draw distant horizon
glLineWidth(3.5F);
glBegin(GL_LINE_STRIP);
glVertex2f(0.0F, 25.0F);
glVertex2f(50.0F, 100.0F);
glVertex2f(100.0F, 25.0F);
glVertex2f(225.0F, 125.0F);
glVertex2f(300.0F, 50.0F);
glVertex2f(375.0F, 100.0F);
glVertex2f(460.0F, 25.0F);
glVertex2f(525.0F, 100.0F);
glVertex2f(600.0F, 20.0F);
glVertex2f(675.0F, 70.0F);
glVertex2f(750.0F, 25.0F);
glVertex2f(800.0F, 90.0F);
glEnd();
glutSwapBuffers();
}
void setupRc(){
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
}
void processMenu(const int option) {
switch (option) {
case 1: // Antialiased Rendering
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
break;
case 2: // Normal Rendering
glDisable(GL_BLEND);
glDisable(GL_POINT_SMOOTH);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
break;
default:
break;
}
std::cout << std::format("Menu option selected: {}, Main window ID: {}", option, mainWindowId) << "\n";
glutSetWindow(mainWindowId);
glutPostRedisplay(); // 请求重新绘制窗口
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 600);
mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
glutCreateMenu(processMenu);
glutAddMenuEntry("Antialiased Rendering",1);
glutAddMenuEntry("Normal Rendering",2);
glutAttachMenu(GLUT_RIGHT_BUTTON);
setupRc();
glutMainLoop();
return 0;
}
windows可以看到效果,mac看不到。
球体世界抗锯齿
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <numbers>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <cmath>
#include <iostream>
struct GLFrame {
GLfloat vLocation[3]; // 位置 -> move x, y, z
GLfloat vUp[3]; // +y
GLfloat vForward[3]; // -z
// GLfloat right[3]; // +x-> 通过 yz叉积计算 -> vUp, vForward均为单位向量
};
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLint NUM_SPHERES = 30;
static constexpr GLfloat ambientLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
static constexpr GLfloat diffuseLight[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -100.0F, 100.0F, 50.0F, 1.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
GLFrame frameCamera; // 摄像机帧
GLFrame spheres[NUM_SPHERES]; // 球体帧
GLfloat shadowMat[16];
void gltInitFrame(GLFrame *frame) {
frame->vLocation[0] = 0.0F;
frame->vLocation[1] = 0.0F;
frame->vLocation[2] = 0.0F;
frame->vUp[0] = 0.0F;
frame->vUp[1] = 1.0F;
frame->vUp[2] = 0.0F;
frame->vForward[0] = 0.0F;
frame->vForward[1] = 0.0F;
frame->vForward[2] = -1.0F; // 相机朝向为-z
}
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(35.0F, aspectRatio, 1.0F, 50.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
// 绕 vUp 轴左右转向(yaw)。正角度 = 向左转(右手系绕 +Y 逆时针)。
// Rodrigues 公式:v' = v·cosθ + (vUp × v)·sinθ(vUp ⟂ vForward,u·(u·v)(1-cosθ) 项为 0)
void rotateCamera(const GLfloat degrees) {
constexpr GLfloat radPerDeg = 3.14159265F / 180.0F;
const GLfloat theta = degrees * radPerDeg;
const GLfloat c = std::cos(theta);
const GLfloat s = std::sin(theta);
const GLfloat fx = frameCamera.vForward[0];
const GLfloat fy = frameCamera.vForward[1];
const GLfloat fz = frameCamera.vForward[2];
frameCamera.vForward[0] = fx * c + (frameCamera.vUp[1] * fz - frameCamera.vUp[2] * fy) * s;
frameCamera.vForward[1] = fy * c + (frameCamera.vUp[2] * fx - frameCamera.vUp[0] * fz) * s;
frameCamera.vForward[2] = fz * c + (frameCamera.vUp[0] * fy - frameCamera.vUp[1] * fx) * s;
// 浮点累加会漂移,重新归一化保持单位长度(前进步长 0.1 才精确)
const GLfloat len = std::sqrt(frameCamera.vForward[0] * frameCamera.vForward[0] +
frameCamera.vForward[1] * frameCamera.vForward[1] +
frameCamera.vForward[2] * frameCamera.vForward[2]);
if (len > 0.0F) {
frameCamera.vForward[0] /= len;
frameCamera.vForward[1] /= len;
frameCamera.vForward[2] /= len;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
frameCamera.vLocation[0] += frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] += frameCamera.vForward[1] * 0.1F; // 0
frameCamera.vLocation[2] += frameCamera.vForward[2] * 0.1F; // 0.1
break;
case GLUT_KEY_DOWN:
frameCamera.vLocation[0] -= frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] -= frameCamera.vForward[1] * 0.1F; //
frameCamera.vLocation[2] -= frameCamera.vForward[2] * 0.1F; // -0.1
break;
case GLUT_KEY_LEFT:
rotateCamera(5.0F); // 向左转(正角度 = 绕 vUp 逆时针)
break;
case GLUT_KEY_RIGHT:
rotateCamera(-5.0F); // 向右转
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
/**
* 计算平面投影阴影矩阵:把顶点沿光线方向投影到平面 planeEq 上
*
* planeEq : 平面方程 a·x + b·y + c·z + d = 0
* lightPos: 与 glLightfv(GL_POSITION) 相同的齐次坐标
* w = 1 点光源(阴影有透视近大远小)
* w = 0 平行光/方向光(阳光,阴影是平行投影)
* m : 输出 16 元素列主序矩阵,可直接传给 glMultMatrixf
*
* 原理:顶点 p 沿光线方向的影子 s = p + t·L 满足 n·s + d = 0,
* 解得 t = -(n·p + d)/(n·L),代回可得齐次形式
* s' = dot·p - (n·p)·L,其中 dot = n·L + d·Lw。
* 这是 p 的线性(射影)变换,故可写成 4×4 矩阵:
* M[i][j] = dot·δ(i,j) - L[i]·n[j],按列主序展开。
*/
void makeShadowMatrix(GLfloat m[16], const GLfloat planeEq[4], const GLfloat lightPos[4]) {
const GLfloat dot = planeEq[0] * lightPos[0] + planeEq[1] * lightPos[1] +
planeEq[2] * lightPos[2] + planeEq[3] * lightPos[3];
m[0] = dot - lightPos[0] * planeEq[0]; // 第 1 列(列主序)
m[1] = - lightPos[1] * planeEq[0];
m[2] = - lightPos[2] * planeEq[0];
m[3] = - lightPos[3] * planeEq[0];
m[4] = - lightPos[0] * planeEq[1]; // 第 2 列
m[5] = dot - lightPos[1] * planeEq[1];
m[6] = - lightPos[2] * planeEq[1];
m[7] = - lightPos[3] * planeEq[1];
m[8] = - lightPos[0] * planeEq[2]; // 第 3 列
m[9] = - lightPos[1] * planeEq[2];
m[10] = dot - lightPos[2] * planeEq[2];
m[11] = - lightPos[3] * planeEq[2];
m[12] = - lightPos[0] * planeEq[3]; // 第 4 列
m[13] = - lightPos[1] * planeEq[3];
m[14] = - lightPos[2] * planeEq[3];
m[15] = dot - lightPos[3] * planeEq[3];
}
void drawGround(){
glColor3f(1.0F, 1.0F, 1.0F); // 绿色
constexpr GLfloat y = -0.4F;
constexpr GLfloat edge = 20.0F;
constexpr GLfloat step = 1.0F;
for (GLfloat i = -edge; i <= edge; i += step) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0F, 1.0F, 0.0F); // All Point up
for (GLfloat j = edge; j >= -edge; j -= step) {
glVertex3f(i, y, j);
glVertex3f(i + step, y, j);
}
glEnd();
}
}
/**
* 物体→世界
* 叉积有方向前面为右手法则,右向量 = Forward × Up
* 右手法则:四指从前向上卷,拇指指向右手的右边,叉积的方向就是拇指的方向。
*/
void gltGetMatrixFromFrame(const GLFrame *pFrame, GLfloat m[16]) {
GLfloat r[3]; // 右向量 X = Forward × Up
r[0] = pFrame->vForward[1] * pFrame->vUp[2] - pFrame->vForward[2] * pFrame->vUp[1];
r[1] = pFrame->vForward[2] * pFrame->vUp[0] - pFrame->vForward[0] * pFrame->vUp[2];
r[2] = pFrame->vForward[0] * pFrame->vUp[1] - pFrame->vForward[1] * pFrame->vUp[0];
// 列主序
// 第3列取 -vForward:OpenGL 的 +Z 指向观察者背后
m[0]=r[0]; m[4]=pFrame->vUp[0]; m[8] =-pFrame->vForward[0]; m[12]=pFrame->vLocation[0];
m[1]=r[1]; m[5]=pFrame->vUp[1]; m[9] =-pFrame->vForward[1]; m[13]=pFrame->vLocation[1];
m[2]=r[2]; m[6]=pFrame->vUp[2]; m[10]=-pFrame->vForward[2]; m[14]=pFrame->vLocation[2];
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
}
/**
* 视图矩阵 = 上面的逆:旋转转置 + 平移取负
* 把世界挪到相机眼前
*/
void gltApplyCameraTransform(const GLFrame *pCamera) {
GLfloat r[3];
r[0] = pCamera->vForward[1] * pCamera->vUp[2] - pCamera->vForward[2] * pCamera->vUp[1];
r[1] = pCamera->vForward[2] * pCamera->vUp[0] - pCamera->vForward[0] * pCamera->vUp[2];
r[2] = pCamera->vForward[0] * pCamera->vUp[1] - pCamera->vForward[1] * pCamera->vUp[0];
GLfloat m[16]; // 同样的轴,改填「行」,即转置
m[0]=r[0]; m[4]=r[1]; m[8] =r[2]; m[12]=0.0F;
m[1]=pCamera->vUp[0]; m[5]=pCamera->vUp[1]; m[9] =pCamera->vUp[2]; m[13]=0.0F;
m[2]=-pCamera->vForward[0]; m[6]=-pCamera->vForward[1]; m[10]=-pCamera->vForward[2]; m[14]=0.0F;
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
glMultMatrixf(m);
glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
void drawSpheres() {
for (int i = 0; i < NUM_SPHERES; ++i) {
glPushMatrix();
GLfloat m[16];
gltGetMatrixFromFrame(&spheres[i], m);
glMultMatrixf(m);
glutSolidSphere(0.3F, 48, 32);
glPopMatrix();
}
}
void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const GLint numMajor, const GLint numMinor) {
glPushMatrix();
glTranslatef(0.0F, 0.1F, -2.5F);
for (GLint i = 0; i < numMajor; ++i) {
const GLfloat u0 = (GLfloat)i / numMajor * 2.0F * PI;
const GLfloat u1 = (GLfloat)(i + 1) / numMajor * 2.0F * PI;
glBegin(GL_TRIANGLE_STRIP);
for (GLint j = 0; j <= numMinor; ++j) {
GLfloat v = (GLfloat)j / numMinor * 2.0F * PI;
GLfloat cosV = cosf(v), sinV = sinf(v);
// 点1 (u0 经线)
GLfloat cu0 = cosf(u0), su0 = sinf(u0);
glNormal3f(cu0 * cosV, su0 * cosV, sinV);
glVertex3f(cu0 * (majorRadius + minorRadius * cosV),
su0 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
// 点2 (u1 经线)
GLfloat cu1 = cosf(u1), su1 = sinf(u1);
glNormal3f(cu1 * cosV, su1 * cosV, sinV);
glVertex3f(cu1 * (majorRadius + minorRadius * cosV),
su1 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
}
glEnd();
}
glPopMatrix();
}
void drawBall(){
glPushMatrix();
glTranslatef(1.0F, 0.1F, -2.5F);
glutSolidSphere(0.1F, 17, 9);
glPopMatrix();
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
gltApplyCameraTransform(&frameCamera);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
// 绘制地面
glColor3f(0.60F, 0.40F, 0.10F);
drawGround();
// 阴影
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glMultMatrixf(shadowMat);
glColor3f(0.0F, 0.0F, 0.0F);
drawSpheres();
drawTorus(0.35, 0.15, 61, 37);
drawBall();
glPopMatrix();
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// 球体、圆环和小球
glColor3f(0.0F, 1.0F, 0.0F);
drawSpheres();
glColor3f(1.0F, 0.0F, 0.0F);
drawTorus(0.35, 0.15, 61, 37);
glColor3f(0.0F, 0.0F, 1.0F);
drawBall();
glPopMatrix();
glutSwapBuffers();
}
void setupRc(){
constexpr GLfloat fNoLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
constexpr GLfloat fLowLight[] = { 0.25F, 0.25F, 0.25F, 1.0F };
glEnable(GL_MULTISAMPLE);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.25F, 0.25F, 0.25F, 1.0F);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// 阴影
constexpr GLfloat planeEq[4] = { 0.0F, 1.0F, 0.0F, 0.4F };
makeShadowMatrix(shadowMat, planeEq, lightPos);
// 初始化摄像机和球体帧
gltInitFrame(&frameCamera);
for (int i = 0; i < NUM_SPHERES; ++i) {
gltInitFrame(&spheres[i]);
// -20.0 到 19.9
spheres[i].vLocation[0] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
spheres[i].vLocation[1] = 0.0F;
spheres[i].vLocation[2] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
}
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_MULTISAMPLE);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
// 验证 MSAA 是否真正生效(macOS 的 GLUT 拿不到多重采样像素格式时会静默降级)
GLint sampleBuffers = 0, samples = 0;
glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);
glGetIntegerv(GL_SAMPLES, &samples);
std::cout << std::format("Renderer: {}, sample buffers: {}, samples: {}",
reinterpret_cast<const char*>(glGetString(GL_RENDERER)), sampleBuffers, samples) << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}
球体世界雾化
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <numbers>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <cmath>
#include <iostream>
struct GLFrame {
GLfloat vLocation[3]; // 位置 -> move x, y, z
GLfloat vUp[3]; // +y
GLfloat vForward[3]; // -z
// GLfloat right[3]; // +x-> 通过 yz叉积计算 -> vUp, vForward均为单位向量
};
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLint NUM_SPHERES = 30;
static constexpr GLfloat ambientLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
static constexpr GLfloat diffuseLight[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -100.0F, 100.0F, 50.0F, 1.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
GLFrame frameCamera; // 摄像机帧
GLFrame spheres[NUM_SPHERES]; // 球体帧
GLfloat shadowMat[16];
void gltInitFrame(GLFrame *frame) {
frame->vLocation[0] = 0.0F;
frame->vLocation[1] = 0.0F;
frame->vLocation[2] = 0.0F;
frame->vUp[0] = 0.0F;
frame->vUp[1] = 1.0F;
frame->vUp[2] = 0.0F;
frame->vForward[0] = 0.0F;
frame->vForward[1] = 0.0F;
frame->vForward[2] = -1.0F; // 相机朝向为-z
}
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(35.0F, aspectRatio, 1.0F, 50.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
// 绕 vUp 轴左右转向(yaw)。正角度 = 向左转(右手系绕 +Y 逆时针)。
// Rodrigues 公式:v' = v·cosθ + (vUp × v)·sinθ(vUp ⟂ vForward,u·(u·v)(1-cosθ) 项为 0)
void rotateCamera(const GLfloat degrees) {
constexpr GLfloat radPerDeg = 3.14159265F / 180.0F;
const GLfloat theta = degrees * radPerDeg;
const GLfloat c = std::cos(theta);
const GLfloat s = std::sin(theta);
const GLfloat fx = frameCamera.vForward[0];
const GLfloat fy = frameCamera.vForward[1];
const GLfloat fz = frameCamera.vForward[2];
frameCamera.vForward[0] = fx * c + (frameCamera.vUp[1] * fz - frameCamera.vUp[2] * fy) * s;
frameCamera.vForward[1] = fy * c + (frameCamera.vUp[2] * fx - frameCamera.vUp[0] * fz) * s;
frameCamera.vForward[2] = fz * c + (frameCamera.vUp[0] * fy - frameCamera.vUp[1] * fx) * s;
// 浮点累加会漂移,重新归一化保持单位长度(前进步长 0.1 才精确)
const GLfloat len = std::sqrt(frameCamera.vForward[0] * frameCamera.vForward[0] +
frameCamera.vForward[1] * frameCamera.vForward[1] +
frameCamera.vForward[2] * frameCamera.vForward[2]);
if (len > 0.0F) {
frameCamera.vForward[0] /= len;
frameCamera.vForward[1] /= len;
frameCamera.vForward[2] /= len;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
frameCamera.vLocation[0] += frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] += frameCamera.vForward[1] * 0.1F; // 0
frameCamera.vLocation[2] += frameCamera.vForward[2] * 0.1F; // 0.1
break;
case GLUT_KEY_DOWN:
frameCamera.vLocation[0] -= frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] -= frameCamera.vForward[1] * 0.1F; //
frameCamera.vLocation[2] -= frameCamera.vForward[2] * 0.1F; // -0.1
break;
case GLUT_KEY_LEFT:
rotateCamera(5.0F); // 向左转(正角度 = 绕 vUp 逆时针)
break;
case GLUT_KEY_RIGHT:
rotateCamera(-5.0F); // 向右转
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
/**
* 计算平面投影阴影矩阵:把顶点沿光线方向投影到平面 planeEq 上
*
* planeEq : 平面方程 a·x + b·y + c·z + d = 0
* lightPos: 与 glLightfv(GL_POSITION) 相同的齐次坐标
* w = 1 点光源(阴影有透视近大远小)
* w = 0 平行光/方向光(阳光,阴影是平行投影)
* m : 输出 16 元素列主序矩阵,可直接传给 glMultMatrixf
*
* 原理:顶点 p 沿光线方向的影子 s = p + t·L 满足 n·s + d = 0,
* 解得 t = -(n·p + d)/(n·L),代回可得齐次形式
* s' = dot·p - (n·p)·L,其中 dot = n·L + d·Lw。
* 这是 p 的线性(射影)变换,故可写成 4×4 矩阵:
* M[i][j] = dot·δ(i,j) - L[i]·n[j],按列主序展开。
*/
void makeShadowMatrix(GLfloat m[16], const GLfloat planeEq[4], const GLfloat lightPos[4]) {
const GLfloat dot = planeEq[0] * lightPos[0] + planeEq[1] * lightPos[1] +
planeEq[2] * lightPos[2] + planeEq[3] * lightPos[3];
m[0] = dot - lightPos[0] * planeEq[0]; // 第 1 列(列主序)
m[1] = - lightPos[1] * planeEq[0];
m[2] = - lightPos[2] * planeEq[0];
m[3] = - lightPos[3] * planeEq[0];
m[4] = - lightPos[0] * planeEq[1]; // 第 2 列
m[5] = dot - lightPos[1] * planeEq[1];
m[6] = - lightPos[2] * planeEq[1];
m[7] = - lightPos[3] * planeEq[1];
m[8] = - lightPos[0] * planeEq[2]; // 第 3 列
m[9] = - lightPos[1] * planeEq[2];
m[10] = dot - lightPos[2] * planeEq[2];
m[11] = - lightPos[3] * planeEq[2];
m[12] = - lightPos[0] * planeEq[3]; // 第 4 列
m[13] = - lightPos[1] * planeEq[3];
m[14] = - lightPos[2] * planeEq[3];
m[15] = dot - lightPos[3] * planeEq[3];
}
void drawGround(){
glColor3f(1.0F, 1.0F, 1.0F); // 绿色
constexpr GLfloat y = -0.4F;
constexpr GLfloat edge = 20.0F;
constexpr GLfloat step = 1.0F;
for (GLfloat i = -edge; i <= edge; i += step) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0F, 1.0F, 0.0F); // All Point up
for (GLfloat j = edge; j >= -edge; j -= step) {
glVertex3f(i, y, j);
glVertex3f(i + step, y, j);
}
glEnd();
}
}
/**
* 物体→世界
* 叉积有方向前面为右手法则,右向量 = Forward × Up
* 右手法则:四指从前向上卷,拇指指向右手的右边,叉积的方向就是拇指的方向。
*/
void gltGetMatrixFromFrame(const GLFrame *pFrame, GLfloat m[16]) {
GLfloat r[3]; // 右向量 X = Forward × Up
r[0] = pFrame->vForward[1] * pFrame->vUp[2] - pFrame->vForward[2] * pFrame->vUp[1];
r[1] = pFrame->vForward[2] * pFrame->vUp[0] - pFrame->vForward[0] * pFrame->vUp[2];
r[2] = pFrame->vForward[0] * pFrame->vUp[1] - pFrame->vForward[1] * pFrame->vUp[0];
// 列主序
// 第3列取 -vForward:OpenGL 的 +Z 指向观察者背后
m[0]=r[0]; m[4]=pFrame->vUp[0]; m[8] =-pFrame->vForward[0]; m[12]=pFrame->vLocation[0];
m[1]=r[1]; m[5]=pFrame->vUp[1]; m[9] =-pFrame->vForward[1]; m[13]=pFrame->vLocation[1];
m[2]=r[2]; m[6]=pFrame->vUp[2]; m[10]=-pFrame->vForward[2]; m[14]=pFrame->vLocation[2];
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
}
/**
* 视图矩阵 = 上面的逆:旋转转置 + 平移取负
* 把世界挪到相机眼前
*/
void gltApplyCameraTransform(const GLFrame *pCamera) {
GLfloat r[3];
r[0] = pCamera->vForward[1] * pCamera->vUp[2] - pCamera->vForward[2] * pCamera->vUp[1];
r[1] = pCamera->vForward[2] * pCamera->vUp[0] - pCamera->vForward[0] * pCamera->vUp[2];
r[2] = pCamera->vForward[0] * pCamera->vUp[1] - pCamera->vForward[1] * pCamera->vUp[0];
GLfloat m[16]; // 同样的轴,改填「行」,即转置
m[0]=r[0]; m[4]=r[1]; m[8] =r[2]; m[12]=0.0F;
m[1]=pCamera->vUp[0]; m[5]=pCamera->vUp[1]; m[9] =pCamera->vUp[2]; m[13]=0.0F;
m[2]=-pCamera->vForward[0]; m[6]=-pCamera->vForward[1]; m[10]=-pCamera->vForward[2]; m[14]=0.0F;
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
glMultMatrixf(m);
glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
void drawSpheres() {
for (int i = 0; i < NUM_SPHERES; ++i) {
glPushMatrix();
GLfloat m[16];
gltGetMatrixFromFrame(&spheres[i], m);
glMultMatrixf(m);
glutSolidSphere(0.3F, 48, 32);
glPopMatrix();
}
}
void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const GLint numMajor, const GLint numMinor) {
glPushMatrix();
glTranslatef(0.0F, 0.1F, -2.5F);
for (GLint i = 0; i < numMajor; ++i) {
const GLfloat u0 = (GLfloat)i / numMajor * 2.0F * PI;
const GLfloat u1 = (GLfloat)(i + 1) / numMajor * 2.0F * PI;
glBegin(GL_TRIANGLE_STRIP);
for (GLint j = 0; j <= numMinor; ++j) {
GLfloat v = (GLfloat)j / numMinor * 2.0F * PI;
GLfloat cosV = cosf(v), sinV = sinf(v);
// 点1 (u0 经线)
GLfloat cu0 = cosf(u0), su0 = sinf(u0);
glNormal3f(cu0 * cosV, su0 * cosV, sinV);
glVertex3f(cu0 * (majorRadius + minorRadius * cosV),
su0 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
// 点2 (u1 经线)
GLfloat cu1 = cosf(u1), su1 = sinf(u1);
glNormal3f(cu1 * cosV, su1 * cosV, sinV);
glVertex3f(cu1 * (majorRadius + minorRadius * cosV),
su1 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
}
glEnd();
}
glPopMatrix();
}
void drawBall(){
glPushMatrix();
glTranslatef(1.0F, 0.1F, -2.5F);
glutSolidSphere(0.1F, 17, 9);
glPopMatrix();
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
gltApplyCameraTransform(&frameCamera);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
// 绘制地面
glColor3f(0.60F, 0.40F, 0.10F);
drawGround();
// 阴影
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glMultMatrixf(shadowMat);
glColor3f(0.0F, 0.0F, 0.0F);
drawSpheres();
drawTorus(0.35, 0.15, 61, 37);
drawBall();
glPopMatrix();
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// 球体、圆环和小球
glColor3f(0.0F, 1.0F, 0.0F);
drawSpheres();
glColor3f(1.0F, 0.0F, 0.0F);
drawTorus(0.35, 0.15, 61, 37);
glColor3f(0.0F, 0.0F, 1.0F);
drawBall();
glPopMatrix();
glutSwapBuffers();
}
void setupRc(){
constexpr GLfloat fNoLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
constexpr GLfloat fLowLight[] = { 0.25F, 0.25F, 0.25F, 1.0F };
glEnable(GL_MULTISAMPLE);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.25F, 0.25F, 0.25F, 1.0F);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// 开启雾化
glEnable(GL_FOG);
glFogfv(GL_FOG_COLOR, fLowLight);
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, 5.0F);
glFogf(GL_FOG_END, 30.0F);
// 阴影
constexpr GLfloat planeEq[4] = { 0.0F, 1.0F, 0.0F, 0.4F };
makeShadowMatrix(shadowMat, planeEq, lightPos);
// 初始化摄像机和球体帧
gltInitFrame(&frameCamera);
for (int i = 0; i < NUM_SPHERES; ++i) {
gltInitFrame(&spheres[i]);
// -20.0 到 19.9
spheres[i].vLocation[0] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
spheres[i].vLocation[1] = 0.0F;
spheres[i].vLocation[2] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
}
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_MULTISAMPLE);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
// 验证 MSAA 是否真正生效(macOS 的 GLUT 拿不到多重采样像素格式时会静默降级)
GLint sampleBuffers = 0, samples = 0;
glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);
glGetIntegerv(GL_SAMPLES, &samples);
std::cout << std::format("Renderer: {}, sample buffers: {}, samples: {}",
reinterpret_cast<const char*>(glGetString(GL_RENDERER)), sampleBuffers, samples) << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}
移动拖尾球体
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <numbers>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <cmath>
#include <iostream>
struct GLFrame {
GLfloat vLocation[3]; // 位置 -> move x, y, z
GLfloat vUp[3]; // +y
GLfloat vForward[3]; // -z
// GLfloat right[3]; // +x-> 通过 yz叉积计算 -> vUp, vForward均为单位向量
};
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLint NUM_SPHERES = 30;
static constexpr GLfloat ambientLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
static constexpr GLfloat diffuseLight[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -100.0F, 100.0F, 50.0F, 1.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
GLFrame frameCamera; // 摄像机帧
GLFrame spheres[NUM_SPHERES]; // 球体帧
GLfloat shadowMat[16];
void gltInitFrame(GLFrame *frame) {
frame->vLocation[0] = 0.0F;
frame->vLocation[1] = 0.0F;
frame->vLocation[2] = 0.0F;
frame->vUp[0] = 0.0F;
frame->vUp[1] = 1.0F;
frame->vUp[2] = 0.0F;
frame->vForward[0] = 0.0F;
frame->vForward[1] = 0.0F;
frame->vForward[2] = -1.0F; // 相机朝向为-z
}
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(35.0F, aspectRatio, 1.0F, 50.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
// 绕 vUp 轴左右转向(yaw)。正角度 = 向左转(右手系绕 +Y 逆时针)。
// Rodrigues 公式:v' = v·cosθ + (vUp × v)·sinθ(vUp ⟂ vForward,u·(u·v)(1-cosθ) 项为 0)
void rotateCamera(const GLfloat degrees) {
constexpr GLfloat radPerDeg = 3.14159265F / 180.0F;
const GLfloat theta = degrees * radPerDeg;
const GLfloat c = std::cos(theta);
const GLfloat s = std::sin(theta);
const GLfloat fx = frameCamera.vForward[0];
const GLfloat fy = frameCamera.vForward[1];
const GLfloat fz = frameCamera.vForward[2];
frameCamera.vForward[0] = fx * c + (frameCamera.vUp[1] * fz - frameCamera.vUp[2] * fy) * s;
frameCamera.vForward[1] = fy * c + (frameCamera.vUp[2] * fx - frameCamera.vUp[0] * fz) * s;
frameCamera.vForward[2] = fz * c + (frameCamera.vUp[0] * fy - frameCamera.vUp[1] * fx) * s;
// 浮点累加会漂移,重新归一化保持单位长度(前进步长 0.1 才精确)
const GLfloat len = std::sqrt(frameCamera.vForward[0] * frameCamera.vForward[0] +
frameCamera.vForward[1] * frameCamera.vForward[1] +
frameCamera.vForward[2] * frameCamera.vForward[2]);
if (len > 0.0F) {
frameCamera.vForward[0] /= len;
frameCamera.vForward[1] /= len;
frameCamera.vForward[2] /= len;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
break;
case GLUT_KEY_DOWN:
break;
case GLUT_KEY_LEFT:
break;
case GLUT_KEY_RIGHT:
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
/**
* 计算平面投影阴影矩阵:把顶点沿光线方向投影到平面 planeEq 上
*
* planeEq : 平面方程 a·x + b·y + c·z + d = 0
* lightPos: 与 glLightfv(GL_POSITION) 相同的齐次坐标
* w = 1 点光源(阴影有透视近大远小)
* w = 0 平行光/方向光(阳光,阴影是平行投影)
* m : 输出 16 元素列主序矩阵,可直接传给 glMultMatrixf
*
* 原理:顶点 p 沿光线方向的影子 s = p + t·L 满足 n·s + d = 0,
* 解得 t = -(n·p + d)/(n·L),代回可得齐次形式
* s' = dot·p - (n·p)·L,其中 dot = n·L + d·Lw。
* 这是 p 的线性(射影)变换,故可写成 4×4 矩阵:
* M[i][j] = dot·δ(i,j) - L[i]·n[j],按列主序展开。
*/
void makeShadowMatrix(GLfloat m[16], const GLfloat planeEq[4], const GLfloat lightPos[4]) {
const GLfloat dot = planeEq[0] * lightPos[0] + planeEq[1] * lightPos[1] +
planeEq[2] * lightPos[2] + planeEq[3] * lightPos[3];
m[0] = dot - lightPos[0] * planeEq[0]; // 第 1 列(列主序)
m[1] = - lightPos[1] * planeEq[0];
m[2] = - lightPos[2] * planeEq[0];
m[3] = - lightPos[3] * planeEq[0];
m[4] = - lightPos[0] * planeEq[1]; // 第 2 列
m[5] = dot - lightPos[1] * planeEq[1];
m[6] = - lightPos[2] * planeEq[1];
m[7] = - lightPos[3] * planeEq[1];
m[8] = - lightPos[0] * planeEq[2]; // 第 3 列
m[9] = - lightPos[1] * planeEq[2];
m[10] = dot - lightPos[2] * planeEq[2];
m[11] = - lightPos[3] * planeEq[2];
m[12] = - lightPos[0] * planeEq[3]; // 第 4 列
m[13] = - lightPos[1] * planeEq[3];
m[14] = - lightPos[2] * planeEq[3];
m[15] = dot - lightPos[3] * planeEq[3];
}
/**
* 物体→世界
* 叉积有方向前面为右手法则,右向量 = Forward × Up
* 右手法则:四指从前向上卷,拇指指向右手的右边,叉积的方向就是拇指的方向。
*/
void gltGetMatrixFromFrame(const GLFrame *pFrame, GLfloat m[16]) {
GLfloat r[3]; // 右向量 X = Forward × Up
r[0] = pFrame->vForward[1] * pFrame->vUp[2] - pFrame->vForward[2] * pFrame->vUp[1];
r[1] = pFrame->vForward[2] * pFrame->vUp[0] - pFrame->vForward[0] * pFrame->vUp[2];
r[2] = pFrame->vForward[0] * pFrame->vUp[1] - pFrame->vForward[1] * pFrame->vUp[0];
// 列主序
// 第3列取 -vForward:OpenGL 的 +Z 指向观察者背后
m[0]=r[0]; m[4]=pFrame->vUp[0]; m[8] =-pFrame->vForward[0]; m[12]=pFrame->vLocation[0];
m[1]=r[1]; m[5]=pFrame->vUp[1]; m[9] =-pFrame->vForward[1]; m[13]=pFrame->vLocation[1];
m[2]=r[2]; m[6]=pFrame->vUp[2]; m[10]=-pFrame->vForward[2]; m[14]=pFrame->vLocation[2];
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
}
/**
* 视图矩阵 = 上面的逆:旋转转置 + 平移取负
* 把世界挪到相机眼前
*/
void gltApplyCameraTransform(const GLFrame *pCamera) {
GLfloat r[3];
r[0] = pCamera->vForward[1] * pCamera->vUp[2] - pCamera->vForward[2] * pCamera->vUp[1];
r[1] = pCamera->vForward[2] * pCamera->vUp[0] - pCamera->vForward[0] * pCamera->vUp[2];
r[2] = pCamera->vForward[0] * pCamera->vUp[1] - pCamera->vForward[1] * pCamera->vUp[0];
GLfloat m[16]; // 同样的轴,改填「行」,即转置
m[0]=r[0]; m[4]=r[1]; m[8] =r[2]; m[12]=0.0F;
m[1]=pCamera->vUp[0]; m[5]=pCamera->vUp[1]; m[9] =pCamera->vUp[2]; m[13]=0.0F;
m[2]=-pCamera->vForward[0]; m[6]=-pCamera->vForward[1]; m[10]=-pCamera->vForward[2]; m[14]=0.0F;
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
glMultMatrixf(m);
glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
void drawGround(){
constexpr GLfloat y = 0.0F;
constexpr GLfloat edge = 20.0F;
constexpr GLfloat step = 0.5F;
GLint count = 0;
glShadeModel(GL_FLAT);
for (GLfloat i = -edge, color = 0.0F; i <= edge; i += step) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0F, 1.0F, 0.0F); // All Point up
for (GLfloat j = edge; j >= -edge; j -= step) {
color = (count % 2 == 0) ? 1.0F : 0.0F;
glColor4f(color, color, color, 0.5F);
glVertex3f(i, y, j);
glVertex3f(i + step, y, j);
count ++ ;
}
glEnd();
}
glShadeModel(GL_SMOOTH);
}
void drawGeometry(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0F, -0.4F, 0.0F);
drawGround();
glColor3f(1.0F, 0.0F, 0.0F);
glTranslatef(0.0F, 0.5F, -3.5F);
glRotatef(-(yRot * 2.0F), 0.0F, 1.0F, 0.0F);
glTranslatef(1.0F, 0.0F, 0.0F);
glutSolidSphere(0.1F, 17, 9);
glPopMatrix();
}
void renderScene(){
GLfloat fPass = 0.0F;
constexpr GLfloat fPasses = 10.0F;
yRot = 35.0F;
for (fPass = 0.0F; fPass < fPasses; fPass += 1.0F) {
yRot += 0.75F; // 1.0F / (fPass + 1.0F)
drawGeometry();
if(fPass == 0.0F){
glAccum(GL_LOAD, 0.5F);
} else {
glAccum(GL_ACCUM, 0.5F * (1.0F / fPasses));
}
}
glAccum(GL_RETURN, 1.0F);
glutSwapBuffers();
}
void setupRc(){
constexpr GLfloat fNoLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
constexpr GLfloat fLowLight[] = { 0.25F, 0.25F, 0.25F, 1.0F };
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.25F, 0.25F, 0.25F, 1.0F);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_ACCUM);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
// 验证 MSAA 是否真正生效(macOS 的 GLUT 拿不到多重采样像素格式时会静默降级)
GLint sampleBuffers = 0, samples = 0;
glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);
glGetIntegerv(GL_SAMPLES, &samples);
std::cout << std::format("Renderer: {}, sample buffers: {}, samples: {}",
reinterpret_cast<const char*>(glGetString(GL_RENDERER)), sampleBuffers, samples) << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}

浙公网安备 33010602011771号