我正在使用 SFML 制作 3D 游戏。我想使用法线来检查三角形是否需要在地形(三角形)网格中绘制。
这是我的代码:
vec3d line1, line2, normal;
line1.x = terrain.tris[i].p[0].x - terrain.tris[i].p[1].x;
line1.y = terrain.tris[i].p[0].y - terrain.tris[i].p[1].y;
line1.z = terrain.tris[i].p[0].z - terrain.tris[i].p[1].z;
line2.x = terrain.tris[i].p[1].x - terrain.tris[i].p[2].x;
line2.y = terrain.tris[i].p[1].y - terrain.tris[i].p[2].y;
line2.z = terrain.tris[i].p[1].z - terrain.tris[i].p[2].z;
normal.x = line1.y * line2.z - line1.z * line2.y;
normal.y = line1.z * line2.x - line1.x * line2.z;
normal.z = line1.x * line2.y - line1.y * line2.x;
vec3d vCameraRay = Vector_Sub(terrain.tris[i].p[0], cam.pos);
if (Vector_DotProduct(normal, vCameraRay) < 0.0f){
do_something();
}
Vector_DotProduct:
float Vector_DotProduct(vec3d& v1, vec3d& v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
向量子:
vec3d Vector_Sub(vec3d& v1, vec3d& v2) {
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
}
vec3d 只是一个包含
float x, y, z;
但是每当我运行程序时,我总是会遇到这个
问题
应该显示的三角形被我的程序认为是“不可见”(它的正常是错误的),但计算对我来说似乎是正确的!
生成三角网格的代码:
for (int i = 0; i < 2; i++) {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
if (x + 1 < wds && y + 1 < wds) {
vec3d point[3];
switch (i) {
case 0:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[1] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x + 1], (float)(x + 1) * scl + p.z };
break;
case 1:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[1] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x], (float)x * scl + p.z };
break;
};
triangle out = { point[0], point[1], point[2] };
tris.push_back(out);
}
}
}
}
wds是网格的大小(边长),scl是每个网格的大小,h是高度图(二维),p是左上角的位置。
我的 3D 点到相机点代码:
float mx = p.x - pos.x;
float my = p.y - pos.y;
float mz = p.z - pos.z;
float dx = cos(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx) - sin(rot.y) * mz;
float dy = sin(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) + cos(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
float dz = cos(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) - sin(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
return { dx, dy, dz };
rot是相机的旋转,p是相机的位置,pos是我要转移到相机点的3D点。
我已经在这个问题上工作了将近一个星期,但似乎没有任何效果。
如果你们能找到问题,那将有很大的帮助。提前致谢!
完整代码
Init.h:
#ifndef _INIT_H_
#define _INIT_H_
#define WIDTH 1200
#define HEIGHT 800
#endif
Noise.h:噪声函数
#pragma once
#ifndef _NOISE_H_
#define _NOISE_H_
extern int primeIndex;
extern int numOctaves;
extern double persistence;
extern int primes[10][3];
#endif
#include <math.h>
float Noise(int i, int x, int y);
float SmoothedNoise(int i, int x, int y);
float Interpolate(float a, float b, float x);
float InterpolatedNoise(int i, float x, float y);
float noise(float x, float y);
噪声.cpp:
#include "Noise.h"
int primeIndex = 0;
int numOctaves = 7;
double persistence = 0.5;
int primes[10][3] = {
{ 995615039, 600173719, 701464987 },
{ 831731269, 162318869, 136250887 },
{ 174329291, 946737083, 245679977 },
{ 362489573, 795918041, 350777237 },
{ 457025711, 880830799, 909678923 },
{ 787070341, 177340217, 593320781 },
{ 405493717, 291031019, 391950901 },
{ 458904767, 676625681, 424452397 },
{ 531736441, 939683957, 810651871 },
{ 997169939, 842027887, 423882827 }
};
float Noise(int i, int x, int y) {
int n = x + y * 57;
n = (n << 13) ^ n;
int a = primes[i][0], b = primes[i][1], c = primes[i][2];
int t = (n * (n * n * a + b) + c) & 0x7fffffff;
return 1.0 - (float)(t) / 1073741824.0;
}
float SmoothedNoise(int i, int x, int y) {
float corners = (Noise(i, x - 1, y - 1) + Noise(i, x + 1, y - 1) +
Noise(i, x - 1, y + 1) + Noise(i, x + 1, y + 1)) / 16,
sides = (Noise(i, x - 1, y) + Noise(i, x + 1, y) + Noise(i, x, y - 1) +
Noise(i, x, y + 1)) / 8,
center = Noise(i, x, y) / 4;
return corners + sides + center;
}
float Interpolate(float a, float b, float x) {
float ft = x * 3.1415927,
f = (1 - cos(ft)) * 0.5;
return a * (1 - f) + b * f;
}
float InterpolatedNoise(int i, float x, float y) {
int integer_X = x;
float fractional_X = x - integer_X;
int integer_Y = y;
float fractional_Y = y - integer_Y;
float v1 = SmoothedNoise(i, integer_X, integer_Y),
v2 = SmoothedNoise(i, integer_X + 1, integer_Y),
v3 = SmoothedNoise(i, integer_X, integer_Y + 1),
v4 = SmoothedNoise(i, integer_X + 1, integer_Y + 1),
i1 = Interpolate(v1, v2, fractional_X),
i2 = Interpolate(v3, v4, fractional_X);
return Interpolate(i1, i2, fractional_Y);
}
float noise(float x, float y) {
float total = 0,
frequency = pow(2, numOctaves),
amplitude = 1;
for (int i = 0; i < numOctaves; ++i) {
frequency /= 2;
amplitude *= persistence;
total += InterpolatedNoise((primeIndex + i) % 10,
x / frequency, y / frequency) * amplitude;
}
return total / frequency;
}
结构.h:
#pragma once
#ifndef _STRUCT_H_
#define _STRUCT_H_
#endif
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <list>
#include "Init.h"
struct vec3d {
float x = 0;
float y = 0;
float z = 0;
};
struct vec2d {
float x = 0;
float y = 0;
};
struct triangle {
vec3d p[3];
int color[3] = { 255, 255, 255 };
vec3d normal;
};
Terrain.h:地形生成
#pragma once
#ifndef _TERRAIN_H_
#define _TERRAIN_H_
#endif
#include <vector>
#include "Struct.h"
#include "Noise.h"
#define wds 50
#define scl 20
#define width 1000
#define height 120
struct Terrain {
public:
std::vector<triangle> tris;
vec3d p = { -width / 2, 0.0f, -width / 2 };
float h[wds][wds];
void triangle_Strip();
};
地形.cpp:
#include "Terrain.h"
void Terrain::make_value() {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
int a = abs(p.z / scl + x), b = abs(p.x / scl + y);
h[y][x] = noise(a, b) * 30;
}
}
}
void Terrain::triangle_Strip() {
tris.clear();
for (int i = 0; i < 2; i++) {
for (int y = 0; y < wds; y++) {
for (int x = 0; x < wds; x++) {
if (x + 1 < wds && y + 1 < wds) {
vec3d point[3];
switch (i) {
case 0:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[1] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x + 1], (float)(x + 1) * scl + p.z };
break;
case 1:
point[0] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x + 1], (float)(x + 1) * scl + p.z };
point[2] = { (float)y * scl + p.x, height * h[y][x], (float)x * scl + p.z };
point[1] = { (float)(y + 1) * scl + p.x, height * h[y + 1][x], (float)x * scl + p.z };
break;
};
triangle out = { point[0], point[1], point[2] };
tris.push_back(out);
}
}
}
}
}
Camera.h:相机类,get3dcoord获取相机点,get2dcoord获取屏幕点
#pragma once
#ifndef _CAMERA_H_
#define _CAMERA_H_
#endif
#include "Mat.h"
#include "Init.h"
#include "Struct.h"
class Cam {
public:
vec3d pos;
vec3d rot;
float fov;
float speed;
Cam(vec3d p, vec3d r, float f, float s);
vec3d get3dcoord(vec3d p);
vec3d get2dcoord(vec3d p);
};
相机.cpp:
#include "Camera.h"
Cam::Cam(vec3d p, vec3d r, float f, float s) {
pos = p;
rot = r;
fov = f;
speed = s;
}
vec3d Cam::get3dcoord(vec3d p) {
float mx = p.x - pos.x;
float my = p.y - pos.y;
float mz = p.z - pos.z;
float dx = cos(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx) - sin(rot.y) * mz;
float dy = sin(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) + cos(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
float dz = cos(rot.x) * (cos(rot.y) * mz + sin(rot.y) * (sin(rot.z) * my + cos(rot.z) * mx)) - sin(rot.x) * (cos(rot.z) * my + sin(rot.z) * mx);
return { dx, dy, dz };
}
vec3d Cam::get2dcoord(vec3d p) {
float e = (float)tan(fov / 2) * (float)(WIDTH / 2);
float x = (WIDTH / 2) + (e * p.x) / p.z;
float y = (HEIGHT / 2) + (e * p.y) / p.z;
return { x, y, 0 };
}
3D引擎.h:主要
#pragma once
#ifndef _3D_ENGINE_H_
#define _3D_ENGINE_H_
#endif
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <stdlib.h>
#include <sstream>
#include <list>
#include "Struct.h"
#include "Camera.h"
#include "Init.h"
#include "Noise.h"
#include "Terrain.h"
#define endl "\n"
void draw_triangle(vec3d p1, vec3d p2, vec3d p3, int color[]);
vec3d Vector_Sub(vec3d& v1, vec3d& v2);
float Vector_DotProduct(vec3d& v1, vec3d& v2);
3D引擎.cpp:
#include "3D engine.h"
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "3D game in progress");
const sf::Vector2i windowCenter(WIDTH / 2, HEIGHT / 2);
Cam cam({ 0.0f, -40.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, 90, 2.0f);
Terrain terrain;
sf::VertexArray TriangleToDraw(sf::Triangles);
void draw_triangle(vec3d p1, vec3d p2, vec3d p3, int color[]) {
sf::VertexArray tri(sf::Triangles, 3);
tri[0].position = sf::Vector2f(p1.x, p1.y);
tri[1].position = sf::Vector2f(p2.x, p2.y);
tri[2].position = sf::Vector2f(p3.x, p3.y);
tri[0].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
tri[1].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
tri[2].color = sf::Color((int)color[0], (int)color[1], (int)color[2]);
TriangleToDraw.append(tri[0]);
TriangleToDraw.append(tri[1]);
TriangleToDraw.append(tri[2]);
}
vec3d Vector_Sub(vec3d& v1, vec3d& v2) {
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
}
float Vector_DotProduct(vec3d& v1, vec3d& v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
int main() {
window.setMouseCursorVisible(false);
sf::Mouse::setPosition(windowCenter, window);
terrain.make_value();
terrain.triangle_Strip();
while (window.isOpen()) {
TriangleToDraw.clear();
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if ((event.type == sf::Event::MouseLeft || event.type == sf::Event::MouseMoved) && sf::Mouse::getPosition(window) != windowCenter) {
sf::Vector2i pos = sf::Mouse::getPosition(window);
int x_a = pos.x;
int y_a = pos.y;
float movex = (float)(x_a - windowCenter.x) / 500.0f;
float movey = (float)(y_a - windowCenter.y) / 500.0f;
cam.rot.x -= movey;
cam.rot.y += movex;
sf::Mouse::setPosition(windowCenter, window);
}
}
float x = sin(cam.rot.y) * cam.speed; float z = cos(cam.rot.y) * cam.speed;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { cam.pos.x -= x; cam.pos.z -= z; /*terrain.p.x -= x; terrain.p.z -= z;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { cam.pos.x += x; cam.pos.z += z; /*terrain.p.x += x; terrain.p.z += z;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { cam.pos.x += z; cam.pos.z -= x; /*terrain.p.x += z; terrain.p.z -= x;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) { cam.pos.x -= z; cam.pos.z += x; /*terrain.p.x -= z; terrain.p.z += x;*/ }
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) cam.pos.y += cam.speed;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LSHIFT)) cam.pos.y -= cam.speed;
window.clear(sf::Color(0, 0, 0));
std::vector<triangle> triangles;
for (int i = 0, len = terrain.tris.size(); i < len; i++) {
std::vector<vec3d> projected(3);
for (int r = 0; r < 3; r++) projected[r] = cam.get3dcoord(terrain.tris[i].p[r]);
vec3d line1, line2, normal;
line1.x = projected[0].x - projected[1].x;
line1.y = projected[0].y - projected[1].y;
line1.z = projected[0].z - projected[1].z;
line2.x = projected[1].x - projected[2].x;
line2.y = projected[1].y - projected[2].y;
line2.z = projected[1].z - projected[2].z;
normal.x = line1.y * line2.z - line1.z * line2.y;
normal.y = line1.z * line2.x - line1.x * line2.z;
normal.z = line1.x * line2.y - line1.y * line2.x;
float l = sqrtf(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
normal.x /= l; normal.y /= l; normal.z /= l;
vec3d vCameraRay1 = Vector_Sub(projected[0], cam.pos);
if (Vector_DotProduct(normal, vCameraRay1) < 0.0f && projected[0].z < 0.0f && projected[1].z < 0.0f && projected[2].z < 0.0f/*avoid points behind the camera to be projected*/) {
vec3d light = { 0.0f, 0.0f, 1.0f };
float lNormal = sqrtf(powf(light.x, 2) + powf(light.y, 2) + powf(light.z, 2));
light.x /= lNormal; light.y /= lNormal; light.z /= lNormal;
float dp = std::max(0.3f, Vector_DotProduct(light, normal));
int c = 255 * dp;
triangles.push_back({projected[0], projected[1], projected[2], {c, c, c}});
}
}
std::sort(triangles.begin(), triangles.end(), [](triangle& t1, triangle& t2)
{
float z1 = (t1.p[0].z + t1.p[1].z + t1.p[2].z) / 3.0f;
float z2 = (t2.p[0].z + t2.p[1].z + t2.p[2].z) / 3.0f;
return z1 < z2;
});
for (triangle tri : triangles) {
draw_triangle(cam.get2dcoord(tri.p[0]), cam.get2dcoord(tri.p[1]), cam.get2dcoord(tri.p[2]), tri.color);
}
window.draw(TriangleToDraw);
window.display();
}
return 0;
}
法线错误的三角形之一:
Normal: -0.08
vCameraRay: -588.2, 19.0, -662.5
Vector Dotproduct: -74.7
Triangle Point1: 19.03, -35.10, -75.69
Triangle Point2: -1.28, -27.57, -92.94
Triangle Point3: -0.96, -25.79, -71.35
Camera position: 2.20, 627.26, 0.03
点积错误的三角形之一:
Normal: 0.59
vCameraRay: 468.41, 13.59, -634.75
Vector DotProduct: -55.05
Triangle Point1: 13.59, -7.29, -55.05
Triangle Point2: 19.19, 7.04, -37.72
Trianlge Point3: 0.00, 9.75, -28.36
Camera pos: 0.00, 627.45, 0.00