Я работаю над OpenGL ray-tracer, который способен загружать файлы obj и трассировать его. Мое приложение загружает файл obj с помощью assimp, а затем отправляет все грани треугольника (вершины и индексы) в фрагментный шейдер с помощью объектов хранения шейдеров. Структура basi c собирается визуализировать результаты для четырехугольника из фрагментного шейдера.
Вот мой вершинный шейдер. Его форма отвечает за расположение пикселя на квад.
#version 460 core
layout(location = 0) in vec2 cCamWindowVertex;
uniform vec3 wLookAt, wRight, wUp;
out vec3 p;
out vec2 pos;
void main()
{
pos=cCamWindowVertex.xy;
gl_Position = vec4(cCamWindowVertex, 0, 1);
p = wLookAt + wRight * cCamWindowVertex.x + cCamWindowVertex.y * wUp;
}
Это фрагментный шейдер с методами, связанными с трассировкой лучей. Существует метод, который проверяет пересечения между гранями луча и треугольника (грань состоит из трех элементов из primitveCoordinates []). Униформа WEye - это позиция камеры.
#version 460 core
layout(std140, binding=2) buffer primitives{
vec3 primitiveCoordinates[];
};
layout(std140, binding=3) buffer indices{
vec3 indicesC[];
};
out vec4 FragColor;
in vec3 p;
uniform vec3 wEye;
struct Light{
vec3 Le, La;
vec3 direction;
vec3 position;
};
uniform Light lights[];
struct Ray{
vec3 orig, dir;
};
struct Hit{
vec3 orig, dir, normal;
float t;
};
Hit rayTriangleIntersect(Ray ray, vec3 v0, vec3 v1, vec3 v2){
Hit hit;
float t; float u; float v;
vec3 v0v1 = v1 - v0;
vec3 v0v2 = v2 - v0;
vec3 pvec = cross(ray.dir, v0v2);
float det = dot(v0v1, pvec);
if (abs(det) < 0.008){
hit.t=-1;
return hit;// Culling is off
}
float invDet = 1 / det;
vec3 tvec = ray.orig - v0;
u = dot(tvec, pvec) * invDet;
if (u < 0 || u > 1){
hit.t=-1;
return hit;
}
vec3 qvec = cross(tvec, v0v1);
v = dot(ray.dir, qvec) * invDet;
if (v < 0 || u + v > 1) {
hit.t=-1;
return hit;
}
hit.t = dot(v0v2, qvec) * invDet;
hit.normal= cross(v0v1, v0v2);
return hit;
}
vec3 getCoordinatefromIndices(float index){
vec3 back;
for (int i=0; i < primitiveCoordinates.length();i++){
if (i==index){
back=primitiveCoordinates[i];
break;
}
}
return back;
}
Hit firstIntersect(Ray ray){
Hit besthit;
besthit.t=-1;
for (int i=0;i<indicesC.length();i++){
vec3 TrianglePointA=getCoordinatefromIndices(indicesC[i].x);
vec3 TrianglePointB=getCoordinatefromIndices(indicesC[i].y);
vec3 TrianglePointC=getCoordinatefromIndices(indicesC[i].z);
Hit hit=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);
if (hit.t>0 && (besthit.t>hit.t || besthit.t<0)){
besthit=hit;
}
}
return besthit;
}
vec3 trace(Ray ray){
vec3 color;
vec3 ka=vec3(0.5215, 0.1745, 0.0215);
Hit hit;
hit=firstIntersect(ray);
if (hit.t==-1){
return lights[0].La;
}
color=lights[0].La*ka;
for (int i=0;i<lights.length();i++){
Ray shadowRay;
shadowRay.orig=hit.orig;
shadowRay.dir=lights[0].direction;
Hit shadowHit=firstIntersect(shadowRay);
if (shadowHit.t<0 || shadowHit.t > length(shadowRay.orig-hit.orig))
color= color+lights[0].Le;
}
}
return color;
}
void main()
{
Ray ray;
ray.orig = wEye;
ray.dir = normalize(p - wEye);
FragColor = vec4(trace(ray), 1);
}
В main есть несколько унифицированных переменных. cpp:
float fov = 145;
glm::vec3 eye = glm::vec3(0, 0, 2);
glm::vec3 vup = glm::vec3(0, 1, 0);
glm::vec3 lookat = glm::vec3(0, 0, 0);
glm::vec3 w = eye - lookat;
float f = length(w);
glm::vec3 right1 = normalize(cross(vup, w)) ;
glm::vec3 up = normalize(cross(w, right1)) ;
У меня есть три вопроса, связанных с этим.
В настоящее время я работаю с простой моделью куба (созданной в blender). Я могу это визуализировать, но при вращении камеры куб растягивается. Я не знаю почему. Вот изображение куба. Если я поверну камеру слишком сильно, она станет такой же, как во втором пи c.
Мой второй вопрос связан с текстурированием. Смогу ли я добавить текстуры obj (из mtl) с такой структурой приложения? Я могу загрузить текстуры с Assimp. Я собираюсь написать texture(texture_diffuse1, TexCoords);
и умножить его на цвет, рассчитанный методами fs, отвечающими за трассировку лучей. Но если грань содержит больше текстур, кроме диффузной, это может быть проблемой для реализации.
Мой третий вопрос: выбрал ли я правильное направление для реализации трассировки лучей в opengl?
В любом случае, вот моя ссылка на источник на github: https://github.com/fox-1942/rayTracerBoros/tree/testforRayTracing (Используемые в настоящее время шейдеры - это те, у которых в именах есть «четверные» окончания).
Любая помощь приветствуется , Спасибо!
Обновление 1:
Я обновил код по совету Rabbid76:
На моем основном. cpp Я обновил детали, отвечающие за область просмотра:
const float SCR_WIDTH = 1280;
const float SCR_HEIGHT = 720;
.
.
.
glm::vec3 eye = glm::vec3(0, 0, 2);
glm::vec3 vup = glm::vec3(0, 1, 0);
glm::vec3 lookat = glm::vec3(0, 0, 0);
glm::vec3 w = eye - lookat;
float aspect = (float)SCR_WIDTH/(float)SCR_HEIGHT;
glm::vec3 right1 = normalize(cross(vup, w))*aspect;
glm::vec3 up = normalize(cross(w, right1))*aspect;
.
.
.
// Then giving the variables to the corresponding uniforms
glUniform3fv(glGetUniformLocation(shaderQuadProgram.getShaderProgram_id(), "wLookAt"), 1, &lookat.x);
glUniform3fv(glGetUniformLocation(shaderQuadProgram.getShaderProgram_id(), "wRight"), 1, &right1.x);
glUniform3fv(glGetUniformLocation(shaderQuadProgram.getShaderProgram_id(), "wUp"), 1, &vup.x);
glUniform3fv(glGetUniformLocation(shaderQuadProgram.getShaderProgram_id(), "wEye"), 1, &eye.x);
К сожалению, модель все еще растянута.
Обновление 2:
Я совершенно уверен, проблема в том, что мне приходится пересчитывать переменные eye
, up
, f
и right1
каждый раз, когда я изменяю одну из координат переменной глаза. Я создал метод пересчета:
void setCamera(float param) {
eye.x += param;
w = eye - lookat;
float f = length(w);
right1 = normalize(cross(vup, w)) * f * tanf(fov / 2);
up = normalize(cross(w, right1)) * f * tanf(fov / 2);
}
Однако что-то все же не так. Мне кажется, что приложение не обновляет униформу в шейдерах, несмотря на то, что я попытался добавить glUniform3fv в метод выше, чтобы обновить eye
, lookat
, right1
и up
. В любом случае, я использую glfw.
Обновление 3:
Я нашел решение, но оно может вращать камеру только горизонтально:
void setCamera(float param) {
eye = glm::vec3(eye.x * cos(param) + eye.z * sin(param), eye.y, -eye.x * sin(param) + eye.z * cos(param)) + lookat;
w = eye - lookat;
float f = length(w);
right1 = normalize(cross(vup, w)) * f * tanf(fov / 2);
up = normalize(cross(w, right1)) * f * tanf(fov / 2);
}
Этот метод должен применяться при нажатии определенной клавиши c. Если кто-то знает, как сделать свободную летающую камеру на двумерном квадроцикле (используя такую структуру), не стесняйтесь поделиться ею.