0

我正在使用 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
4

0 回答 0