C++实现Gouraud着色算法 - 平滑三角形表面渲染
Gouraud算法是一种用于计算三角形表面上每个顶点颜色的算法,以产生平滑的表面着色效果。它的实现步骤如下:
- 对于每个三角形,计算每个顶点的颜色值。假设每个顶点已经有了一个法向量和一个颜色值,可以使用Phong光照模型计算每个顶点的颜色值。具体来说,可以使用以下公式计算每个顶点的颜色值:
color = ambient + diffuse + specular
其中,ambient是环境光照颜色,diffuse是漫反射颜色,specular是镜面反射颜色。
- 对于每个三角形,通过插值计算出三角形内每个像素的颜色值。具体来说,可以对每个像素的深度值进行插值,然后使用下面的公式计算每个像素的颜色值:
color = (1 - u - v) * color1 + u * color2 + v * color3
其中,color1、color2和color3是三角形的三个顶点的颜色值,u和v是像素在三角形内的插值系数。
- 在渲染三角形时,通过对每个像素进行颜色插值,产生平滑的表面着色效果。
下面是一个简单的C++实现示例:
#include <iostream>
#include <cmath>
using namespace std;
struct Vector3 {
double x, y, z;
Vector3() {}
Vector3(double x, double y, double z) : x(x), y(y), z(z) {}
Vector3 operator+(const Vector3& v) const {
return Vector3(x + v.x, y + v.y, z + v.z);
}
Vector3 operator-(const Vector3& v) const {
return Vector3(x - v.x, y - v.y, z - v.z);
}
Vector3 operator*(double scalar) const {
return Vector3(x * scalar, y * scalar, z * scalar);
}
double length() const {
return sqrt(x * x + y * y + z * z);
}
Vector3 normalize() const {
double len = length();
return Vector3(x / len, y / len, z / len);
}
double dot(const Vector3& v) const {
return x * v.x + y * v.y + z * v.z;
}
Vector3 cross(const Vector3& v) const {
return Vector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
}
};
struct Color {
double r, g, b;
Color() {}
Color(double r, double g, double b) : r(r), g(g), b(b) {}
Color operator+(const Color& c) const {
return Color(r + c.r, g + c.g, b + c.b);
}
Color operator*(double scalar) const {
return Color(r * scalar, g * scalar, b * scalar);
}
};
struct Vertex {
Vector3 position;
Vector3 normal;
Color color;
};
struct Triangle {
Vertex v1, v2, v3;
};
struct Pixel {
int x, y;
double z;
Color color;
};
Color calculateAmbientColor(const Color& materialColor, const Color& lightColor, double ambientIntensity) {
return materialColor * lightColor * ambientIntensity;
}
Color calculateDiffuseColor(const Color& materialColor, const Color& lightColor, const Vector3& lightDirection, const Vector3& normal) {
double dotProduct = max(0.0, lightDirection.dot(normal));
return materialColor * lightColor * dotProduct;
}
Color calculateSpecularColor(const Color& lightColor, const Vector3& lightDirection, const Vector3& viewDirection, const Vector3& normal, double shininess) {
Vector3 reflectionDirection = (normal * 2.0 * lightDirection.dot(normal)) - lightDirection;
double dotProduct = max(0.0, reflectionDirection.dot(viewDirection));
return lightColor * pow(dotProduct, shininess);
}
Color calculateVertexColor(const Vertex& vertex, const Vector3& lightDirection, const Vector3& viewDirection, const Color& lightColor, double ambientIntensity, double shininess) {
Color ambient = calculateAmbientColor(vertex.color, lightColor, ambientIntensity);
Color diffuse = calculateDiffuseColor(vertex.color, lightColor, lightDirection, vertex.normal);
Color specular = calculateSpecularColor(lightColor, lightDirection, viewDirection, vertex.normal, shininess);
return ambient + diffuse + specular;
}
void calculateTriangleVerticesColor(const Triangle& triangle, const Vector3& lightDirection, const Vector3& viewDirection, const Color& lightColor, double ambientIntensity, double shininess, Color& color1, Color& color2, Color& color3) {
color1 = calculateVertexColor(triangle.v1, lightDirection, viewDirection, lightColor, ambientIntensity, shininess);
color2 = calculateVertexColor(triangle.v2, lightDirection, viewDirection, lightColor, ambientIntensity, shininess);
color3 = calculateVertexColor(triangle.v3, lightDirection, viewDirection, lightColor, ambientIntensity, shininess);
}
void rasterizeTriangle(const Triangle& triangle, int width, int height, double* depthBuffer, Pixel* pixelBuffer) {
Color color1, color2, color3;
Vector3 lightDirection(0.0, 0.0, -1.0);
Vector3 viewDirection(0.0, 0.0, 1.0);
Color lightColor(1.0, 1.0, 1.0);
double ambientIntensity = 0.1;
double shininess = 32.0;
calculateTriangleVerticesColor(triangle, lightDirection, viewDirection, lightColor, ambientIntensity, shininess, color1, color2, color3);
Vector3 v1 = triangle.v1.position;
Vector3 v2 = triangle.v2.position;
Vector3 v3 = triangle.v3.position;
if (v1.y == v2.y && v1.y == v3.y) {
return;
}
if (v1.y > v2.y) {
swap(v1, v2);
swap(color1, color2);
}
if (v1.y > v3.y) {
swap(v1, v3);
swap(color1, color3);
}
if (v2.y > v3.y) {
swap(v2, v3);
swap(color2, color3);
}
double totalHeight = v3.y - v1.y;
for (int i = 0; i < totalHeight; i++) {
bool secondHalf = i > v2.y - v1.y || v2.y == v1.y;
double segmentHeight = secondHalf ? v3.y - v2.y : v2.y - v1.y;
double alpha = (double)i / totalHeight;
double beta = (double)(i - (secondHalf ? v2.y - v1.y : 0)) / segmentHeight;
Vector3 A = v1 + (v3 - v1) * alpha;
Vector3 B = secondHalf ? v2 + (v3 - v2) * beta : v1 + (v2 - v1) * beta;
if (A.x > B.x) {
swap(A, B);
}
for (int j = A.x; j <= B.x; j++) {
double phi = B.x == A.x ? 1.0 : (double)(j - A.x) / (double)(B.x - A.x);
Vector3 P = A + (B - A) * phi;
int index = j + i * width;
if (P.z < depthBuffer[index]) {
depthBuffer[index] = P.z;
Color color = secondHalf ? color3 * phi + color2 * (1.0 - phi) : color1 * (1.0 - phi) + color2 * phi;
Pixel pixel;
pixel.x = j;
pixel.y = i;
pixel.z = P.z;
pixel.color = color;
pixelBuffer[index] = pixel;
}
}
}
}
int main() {
int width = 800;
int height = 600;
double* depthBuffer = new double[width * height];
Pixel* pixelBuffer = new Pixel[width * height];
for (int i = 0; i < width * height; i++) {
depthBuffer[i] = 1.0;
}
Triangle triangle;
triangle.v1.position = Vector3(200.0, 200.0, 0.0);
triangle.v2.position = Vector3(400.0, 400.0, 0.0);
triangle.v3.position = Vector3(600.0, 200.0, 0.0);
triangle.v1.normal = Vector3(0.0, 0.0, 1.0);
triangle.v2.normal = Vector3(0.0, 0.0, 1.0);
triangle.v3.normal = Vector3(0.0, 0.0, 1.0);
triangle.v1.color = Color(1.0, 0.0, 0.0);
triangle.v2.color = Color(0.0, 1.0, 0.0);
triangle.v3.color = Color(0.0, 0.0, 1.0);
rasterizeTriangle(triangle, width, height, depthBuffer, pixelBuffer);
for (int i = 0; i < width * height; i++) {
if (depthBuffer[i] < 1.0) {
cout << '(' << pixelBuffer[i].x << ', ' << pixelBuffer[i].y << '): ' << pixelBuffer[i].color.r << ', ' << pixelBuffer[i].color.g << ', ' << pixelBuffer[i].color.b << endl;
}
}
delete[] depthBuffer;
delete[] pixelBuffer;
return 0;
}
原文地址: https://www.cveoy.top/t/topic/lRuX 著作权归作者所有。请勿转载和采集!