Как генерировать лучи камеры для литья лучей - PullRequest
1 голос
/ 16 апреля 2020

Я пытаюсь сделать простой воксельный движок с OpenGL и C ++. Мой первый шаг - отправить лучи из камеры и определить, пересекался ли луч с чем-либо (для тестирования это всего две плоскости). Я работал без вращения камеры, создав полноэкранный четырехугольник и запрограммировав фрагментный шейдер для отправки луча для каждого фрагмента (сейчас я просто предполагаю, что фрагмент является пикселем), который находится в направлении texCoord. х, texCoord.y, -1. Теперь я пытаюсь реализовать вращение камеры.

Я попытался сгенерировать матрицу вращения внутри процессора и отправить ее в шейдер, который будет умножать ее на каждый луч. Однако, когда я поворачиваю камеру, самолеты начинают растягиваться так, как я могу описать только в этом видео. https://www.youtube.com/watch?v=6NScMwnPe8c

Вот код, который создает матрицу и запускается каждый кадр:

float pi = 3.141592;
// camRotX and Y are defined elsewhere and can be controlled from the keyboard during runtime.
glm::vec3 camEulerAngles = glm::vec3(camRotX, camRotY, 0);

std::cout << "X: " << camEulerAngles.x << " Y: " << camEulerAngles.y << "\n";

// Convert to radians
camEulerAngles.x = camEulerAngles.x * pi / 180;
camEulerAngles.y = camEulerAngles.y * pi / 180;
camEulerAngles.z = camEulerAngles.z * pi / 180;

// Generate Quaternian
glm::quat camRotation;
camRotation = glm::quat(camEulerAngles);

// Generate rotation matrix from quaternian
glm::mat4 camToWorldMatrix = glm::toMat4(camRotation);
// No transformation matrix is created because the rays should be relative to 0,0,0

// Send the rotation matrix to the shader
int camTransformMatrixID = glGetUniformLocation(shader, "cameraTransformationMatrix");
glUniformMatrix4fv(camTransformMatrixID, 1, GL_FALSE, glm::value_ptr(camToWorldMatrix));

И фрагментный шейдер:

#version 330 core
in vec4 texCoord;

layout(location = 0) out vec4 color;
uniform vec3 cameraPosition;
uniform vec3 cameraTR;
uniform vec3 cameraTL;
uniform vec3 cameraBR;
uniform vec3 cameraBL;
uniform mat4 cameraTransformationMatrix;
uniform float fov;
uniform float aspectRatio;
float pi = 3.141592;

int RayHitCell(vec3 origin, vec3 direction, vec3 cellPosition, float cellSize)
{
    if(direction.z != 0)
    {
        float multiplicationFactorFront = cellPosition.z - origin.z;
        if(multiplicationFactorFront > 0){
            vec2 interceptFront = vec2(direction.x * multiplicationFactorFront + origin.x,
                                       direction.y * multiplicationFactorFront + origin.y);
            if(interceptFront.x > cellPosition.x && interceptFront.x < cellPosition.x + cellSize &&
               interceptFront.y > cellPosition.y && interceptFront.y < cellPosition.y + cellSize)
            {
                return 1;
            }
        }
        float multiplicationFactorBack = cellPosition.z + cellSize - origin.z;
        if(multiplicationFactorBack > 0){
            vec2 interceptBack = vec2(direction.x * multiplicationFactorBack + origin.x,
                                      direction.y * multiplicationFactorBack + origin.y);
            if(interceptBack.x > cellPosition.x && interceptBack.x < cellPosition.x + cellSize &&
               interceptBack.y > cellPosition.y && interceptBack.y < cellPosition.y + cellSize)
            {
                return 2;
            }
        }
    }
    return 0;
}

void main()
{
    // For now I'm not accounting for FOV and aspect ratio because I want to get the rotation working first
    vec4 beforeRotateRayDirection = vec4(texCoord.x,texCoord.y,-1,0);
    // Apply the rotation matrix that was generated on the cpu
    vec3 rayDirection = vec3(cameraTransformationMatrix *  beforeRotateRayDirection);

    int t = RayHitCell(cameraPosition, rayDirection, vec3(0,0,5), 1);
    if(t == 1)
    {
        // Hit front plane
        color = vec4(0, 0, 1, 0);
    }else if(t == 2)
    {
        // Hit back plane
        color = vec4(0, 0, 0.5, 0);
    }else{
        // background color
        color = vec4(0, 1, 0, 0);
    }
}

1 Ответ

2 голосов
/ 17 апреля 2020

Хорошо. Очень трудно понять, что не так, я попробую все же.

Вот несколько советов и замечаний:

1) Вы можете отлаживать указания, сопоставляя их с цветом RGB. Имейте в виду, что вы должны нормализовать векторы и карту от (-1,1) до (0,1). Просто делай вещи типа dir*0.5+1.0. Пример:

color = vec4(normalize(rayDirection) * 0.5, 0) + vec4(1);

2) Вы можете получить матрицу вращения более прямым способом. Кватернион инициализируется из прямого направления, сначала он будет вращаться вокруг оси Y (горизонтальный вид), затем и только затем вокруг оси X (вертикальный вид). Имейте в виду, что порядок вращения зависит от реализации, если вы инициализируете с углов Эйлера. Используйте mat4_cast, чтобы по возможности избежать экспериментального расширения glm (gtx). Пример:

// Define rotation quaternion starting from look rotation
glm::quat camRotation = glm::vec3(0, 0, 0);
camRotation = glm::rotate(camRotation, glm::radians(camRotY), glm::vec3(0, 1, 0));
camRotation = glm::rotate(camRotation, glm::radians(camRotX), glm::vec3(1, 0, 0));
glm::mat4 camToWorldMatrix = glm::mat4_cast(camRotation);

3) Ваш beforeRotateRayDirection - это вектор, который (вероятно) указывает от (-1, -1, -1) вплоть до (1,1, -1). Что не нормализовано , длина (1,1,1) составляет √3 ≈ 1.7320508075688772 ... Убедитесь, что вы учли это для своей математики столкновений или просто нормализовали вектор.

Пока что мой частичный ответ ...

Ваш тест на столкновение немного странный ... Похоже, вы хотите навести луч в плоскость Z для данной позиции ячейки (но дважды, один для спереди и один сзади). Я рассмотрел ваш код logi c, и он имеет некоторый смысл, но без вершинной программы, поэтому, не зная, каковы значения диапазона texCoord, невозможно быть уверенным. Возможно, вы захотите переосмыслить свою логику c примерно так:

int RayHitCell(vec3 origin, vec3 direction, vec3 cellPosition, float cellSize)
{
    //Get triangle side vectors
    vec3 tu = vec3(cellSize,0,0);   //Triangle U component
    vec3 tv = vec3(0,cellSize,0);   //Triangle V component

    //Determinant for inverse matrix
    vec3 q = cross(direction, tv);
    float det = dot(tu, q);
    //if(abs(det) < 0.0000001) //If too close to zero
    //  return;
    float invdet = 1.0/det;

    //Solve component parameters
    vec3 s = origin - cellPosition;
    float u = dot(s, q) * invdet;
    if(u < 0.0 || u > 1.0)
        return 0;

    vec3 r = cross(s, tu);
    float v = dot(direction, r) * invdet;
    if(v < 0.0 || v > 1.0)
        return 0;

    float t = dot(tv, r) * invdet;
    if(t <= 0.0)
        return 0;

    return 1;
}

void main()
{
    // For now I'm not accounting for FOV and aspect ratio because I want to get the 
    // rotation working first
    vec4 beforeRotateRayDirection = vec4(texCoord.x, texCoord.y, -1, 0);
    // Apply the rotation matrix that was generated on the cpu
    vec3 rayDirection = vec3(cameraTransformationMatrix * beforeRotateRayDirection);

    int t = RayHitCell(cameraPosition, normalize(rayDirection), vec3(0,0,5), 1);
    if (t == 1)
    {
        // Hit front plane
        color = vec4(0, 0, 1, 0);
    }
    else
    {
        // background color
        color = vec4(0, 1, 0, 0);
    }
}

Это должно дать вам самолет, дайте мне знать, если он работает. Куб будет очень легко сделать.

PS .: u и v могут использоваться для наложения текстуры.

...