working ray_tracing.cpp

1. working ray-tracing for spheres

adding nav rn
This commit is contained in:
kavan 2025-03-16 11:37:50 -04:00 committed by GitHub
parent 5ab597aabb
commit ee2af4d83f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -8,208 +8,60 @@
#include <cmath>
using namespace glm;
// vars
// global vars
const int WIDTH = 800;
const int HEIGHT = 600;
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
float lastX = 400.0, lastY = 300.0;
float cameraYaw = -90;
float cameraPitch = 0.0;
float deltaTime = 0.0;
float lastFrame = 0.0;
// functions
GLFWwindow* StartGLU();
GLuint CreateShaderProgram();
GLuint setupQuad();
void renderScene(GLFWwindow* &window, GLuint &quadVAO, GLuint &texture, GLuint &shaderProgram, std::vector<unsigned char> &pixels);
GLuint loadTexture();
void UpdateCam(GLuint shaderProgram, glm::vec3 cameraPos);
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// structures and classes :D
struct Ray{
vec3 direction;
vec3 origin;
Ray(vec3 direction, vec3 origin) : direction(direction), origin(origin){}
};
struct Object{
vec3 centre;
float radius;
vec3 color;
Object(vec3 centre, float radius, vec3 color) : centre(centre), radius(radius), color(color){}
bool intersect(const Ray &ray, float& t){
vec3 oc = ray.origin - centre;
double a = dot(ray.direction, ray.direction);
double b = 2.0 * dot(oc, ray.direction);
double c = dot(oc, oc) - radius * radius;
double discriminant = b*b - 4 * a * c;
if (discriminant < 0) return false;
float temp = (-b - sqrt(discriminant)) / (2.0f*a);
if (temp < 0) {
temp = (-b + sqrt(discriminant)) / (2.0f*a);
if (temp < 0) return false;
}
t = temp;
return true;
}
vec3 getNormal(const glm::vec3& point) const {
return normalize(point - centre);
}
};
class Scene{
class Engine{
public:
std::vector<Object> objs;
vec3 lightPos;
Scene() : lightPos(5.0f, 5.0f, 5.0f) {}
// -- Quad & Texture render
GLFWwindow* window;
GLuint quadVAO;
GLuint texture;
GLuint shaderProgram;
vec3 trace(const Ray& ray) const{
float closest = INFINITY;
const Object* hitObject = nullptr;
for(const auto& obj : objs){
float t; // distance to intersection
// Create non-const copy to call intersect
Object mutableObj = obj;
if(mutableObj.intersect(ray, t)) {
if(t < closest) {
closest = t;
hitObject = &obj;
}
}
};
if (hitObject) {
vec3 hitPoint = ray.origin + ray.direction * closest;
vec3 normal = hitObject->getNormal(hitPoint);
vec3 lightDir = normalize(lightPos - hitPoint);
float diff = std::max(glm::dot(normal, lightDir), 0.0f);
Ray shadowRay(lightDir, hitPoint + normal * 0.001f);
bool inShadow = false;
for (const auto& obj : objs) {
float t;
Object mutableObj = obj; // Create non-const copy
if(mutableObj.intersect(shadowRay, t)) {
inShadow = true;
break;
}
}
glm::vec3 color = hitObject->color;
float ambient = 0.1f;
if (inShadow) {
return color * ambient;
}
return color * (ambient + diff * 0.9f);
}
return vec3(0.0f);
Engine(){
this->window = StartGLFW();
this->shaderProgram = CreateShaderProgram();
auto result = QuadVAO();
this->quadVAO = result[0];
this->texture = result[1];
}
};
GLFWwindow* StartGLFW(){
if(!glfwInit()){
std::cerr<<"glfw failed init, PANIC PANIC!"<<std::endl;
return nullptr;
}
// --------- main --------- //
int main() {
// setup
GLFWwindow* window = StartGLU();
Scene scene;
GLuint shaderProgram = CreateShaderProgram(); // compile shader program
GLuint quadVAO = setupQuad(); // create quad background
GLuint texture = loadTexture();
glfwSetKeyCallback(window, keyCallback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "ray tracer", NULL, NULL);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW." << std::endl;
glfwTerminate();
return nullptr;
}
std::vector<unsigned char> pixels(WIDTH * HEIGHT * 3);
scene.objs = {
Object(glm::vec3(-1.0f, 0.0f, -5.0f), 1.0f, vec3(1.0f, 0.0, 0.0)),
Object(glm::vec3(2.0f, 0.0f, -6.0f), 1.0f, vec3(0.0f, 1.0, 1.0))
glViewport(0, 0, WIDTH, HEIGHT);
return window;
};
while(!glfwWindowShouldClose(window)){
glClear(GL_COLOR_BUFFER_BIT);
UpdateCam(shaderProgram, cameraPos);
GLuint CreateShaderProgram(){
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec2 aPos; // Changed to vec2
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 0.0, 1.0); // Explicit z=0
TexCoord = aTexCoord;
})";
// ray tracing
for(int y = 0; y < HEIGHT; ++y){
for(int x = 0; x < WIDTH; ++x){
float u = float(x) / float(WIDTH); // normalize 0-1
float v = float(y) / float(HEIGHT); // normalize 0-1
vec3 cameraRight = normalize(cross(cameraFront, cameraUp));
float aspectRatio = float(WIDTH) / float(HEIGHT);
float fov = 45.0f;
float halfHeight = tan(radians(fov / 2.0f));
float halfWidth = aspectRatio * halfHeight;
vec3 direction = normalize(
cameraFront
+ (2.0f * u - 1.0f) * halfWidth * cameraRight
+ (1.0f - 2.0f * v) * halfHeight * cameraUp
);
Ray ray(glm::normalize(direction), cameraPos);
vec3 color = scene.trace(ray);
int index = (y * WIDTH + x) * 3;
pixels[index + 0] = static_cast<unsigned char>(color.r * 255);
pixels[index + 1] = static_cast<unsigned char>(color.g * 255);
pixels[index + 2] = static_cast<unsigned char>(color.b * 255);
}
}
// actualize scene
renderScene(window, quadVAO, texture, shaderProgram, pixels);
}
glfwTerminate();
}
// function declarations
GLFWwindow* StartGLU() {
if (!glfwInit()) {
std::cerr << "Failed to initialize GLFW" << std::endl;
return nullptr;
}
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "RAY_TRACING", nullptr, nullptr);
if (!window) {
std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return nullptr;
}
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
std::cerr << "Failed to initialize GLEW." << std::endl;
glfwTerminate();
return nullptr;
}
glViewport(0, 0, WIDTH, HEIGHT);
return window;
}
GLuint CreateShaderProgram(){
const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
})";
const char* fragmentShaderSource = R"(
const char* fragmentShaderSource = R"(
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
@ -218,110 +70,223 @@ GLuint CreateShaderProgram(){
FragColor = texture(screenTexture, TexCoord);
})";
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
// vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
// fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return shaderProgram;
}
GLuint setupQuad() {
float quadVertices[] = {
// positions // texCoords
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
return shaderProgram;
};
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
std::vector<GLuint> QuadVAO(){
float quadVertices[] = {
// positions // texCoords
-1.0f, 1.0f, 0.0f, 1.0f, // top left
-1.0f, -1.0f, 0.0f, 0.0f, // bottom left
1.0f, -1.0f, 1.0f, 0.0f, // bottom right
return VAO;
}
GLuint loadTexture(){
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return texture;
-1.0f, 1.0f, 0.0f, 1.0f, // top left
1.0f, -1.0f, 1.0f, 0.0f, // bottom right
1.0f, 1.0f, 1.0f, 1.0f // top right
};
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
std::vector<GLuint> VAOtexture = {VAO, texture};
return VAOtexture;
}
void renderScene(std::vector<unsigned char> pixels) {
// update texture w/ ray-tracing results
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB,
GL_UNSIGNED_BYTE, pixels.data());
// clear screen and draw textured quad
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
GLint textureLocation = glGetUniformLocation(shaderProgram, "screenTexture");
glUniform1i(textureLocation, 0);
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
};
};
void renderScene(GLFWwindow* &window, GLuint &quadVAO, GLuint &texture, GLuint &shaderProgram, std::vector<unsigned char> &pixels){
// Update texture with ray traced result
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH, HEIGHT, 0, GL_RGB,
GL_UNSIGNED_BYTE, pixels.data());
glUseProgram(shaderProgram);
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
struct Ray{
vec3 direction;
vec3 origin;
Ray(vec3 o, vec3 d) : origin(o), direction(normalize(d)){}
};
struct Material{
vec3 color;
float specular;
float emission;
Material(vec3 c, float s, float e) : color(c), specular(s), emission(e) {}
};
struct Object{
vec3 centre;
float radius;
Material material;
Object(vec3 c, float r, Material m) : centre(c), radius(r), material(m) {}
bool Intersect(Ray &ray, float &t){
vec3 oc = ray.origin - centre;
float a = glm::dot(ray.direction, ray.direction); // ray direction scale by t
float b = 2.0f * glm::dot(oc, ray.direction); //
float c = glm::dot(oc, oc) - radius * radius; // adjustment by sphere radius
double discriminant = b*b - 4*a*c;
if(discriminant < 0){return false;} // no intersection with sphere
float intercept = (-b - sqrt(discriminant)) / (2.0f*a);
if(intercept < 0){
intercept = (-b + sqrt(discriminant)) / (2.0f*a);
if(intercept<0){return false;} // intersection is behind origin
}
t = intercept;
return true;
};
vec3 getNormal(vec3 &point) const{
return normalize(point - centre);
}
};
class Scene {
public:
std::vector<Object> objs;
vec3 lightPos;
Scene() : lightPos(5.0f, 5.0f, 5.0f) {}
vec3 trace(Ray &ray){
float closest = INFINITY;
const Object* hitObj = nullptr;
for(auto& obj : objs){
float t; // distance to intersection
if(obj.Intersect(ray, t)){
if(t < closest) {
closest = t;
hitObj = &obj;
}
}
};
if(hitObj){
vec3 hitPoint = ray.origin + ray.direction * closest; // point on obj hit by ray
vec3 normal = hitObj->getNormal(hitPoint);
vec3 lightDir = normalize(lightPos - hitPoint); // direction light to hitpoint
float diff = std::max(glm::dot(normal, lightDir), 0.0f); // diffuse lighting
Ray shadowRay(hitPoint + normal * 0.001f, lightDir); // slightly up to avoid errors ;P
// check if is in shadow
bool inShadow = false;
// Actually check for shadows by testing if any object blocks light
for(auto& obj : objs) {
float t;
if(obj.Intersect(shadowRay, t)) {
inShadow = true;
break;
}
}
vec3 color = hitObj->material.color;
float ambient = 0.1f; // minimum light level
if (inShadow) {
return color * ambient;
}
return color * (ambient + diff * 0.9f);
}
return vec3(0.0f, 0.0f, 0.1f);
}
};
// --- main loop ---- //
int main(){
Engine engine;
Scene scene;
scene.objs = {
Object(vec3(0.0f, 0.0f, -5.0f), 2.0f, Material(vec3(1.0f, 0.2f, 0.2f), 0.5f, 0.0f)), // Moved further back and made bigger
Object(vec3(3.0f, 0.0f, -7.0f), 1.5f, Material(vec3(0.2f, 1.0f, 0.2f), 0.5f, 0.0f)) // Adjusted position and size
};
// -- loop -- //
std::vector<unsigned char> pixels(WIDTH * HEIGHT * 3);
while(!glfwWindowShouldClose(engine.window)){
glClear(GL_COLOR_BUFFER_BIT);
// render texture (pxl by pxl)
for(int y = 0; y < HEIGHT; ++y){
for(int x = 0; x < WIDTH; ++x){
float aspectRatio = float(WIDTH) / float(HEIGHT);
float u = float(x) / float(WIDTH);
float v = float(y) / float(HEIGHT);
// direction of ray threw camera
vec3 direction(
(2.0f * u - 1.0f) * aspectRatio,
-(2.0f * v - 1.0f), // Flipped to correct orientation
-1.0f // Forward direction (negative z)
);
Ray ray(vec3(0.0f, 0.0f, 0.0f), normalize(direction));
vec3 color = scene.trace(ray);
int index = (y * WIDTH + x) * 3;
pixels[index + 0] = static_cast<unsigned char>(color.r * 255);
pixels[index + 1] = static_cast<unsigned char>(color.g * 255);
pixels[index + 2] = static_cast<unsigned char>(color.b * 255);
}
}
engine.renderScene(pixels);
}
glfwTerminate();
}
void UpdateCam(GLuint shaderProgram, glm::vec3 cameraPos) {
// Calculate current frame time for smooth movement
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
}
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
float cameraSpeed = 1.0f * deltaTime;
if (key == GLFW_KEY_W && (action == GLFW_PRESS || action == GLFW_REPEAT))
cameraPos += cameraSpeed * cameraFront;
if (key == GLFW_KEY_S && (action == GLFW_PRESS || action == GLFW_REPEAT))
cameraPos -= cameraSpeed * cameraFront;
if (key == GLFW_KEY_A && (action == GLFW_PRESS || action == GLFW_REPEAT))
cameraPos -= normalize(cross(cameraFront, cameraUp)) * cameraSpeed;
if (key == GLFW_KEY_D && (action == GLFW_PRESS || action == GLFW_REPEAT))
cameraPos += normalize(cross(cameraFront, cameraUp)) * cameraSpeed;
if (key == GLFW_KEY_Q && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
float xoffset = xpos - lastX;
float yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
// func dec's
float sensitivity = 0.1f;
xoffset *= sensitivity;
yoffset *= sensitivity;
cameraYaw += xoffset;
cameraPitch += -yoffset;
if(cameraPitch > 89.0f) cameraPitch = 89.0f;
if(cameraPitch < -89.0f) cameraPitch = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(cameraYaw)) * cos(glm::radians(cameraPitch));
front.y = sin(glm::radians(cameraPitch));
front.z = sin(glm::radians(cameraYaw)) * cos(glm::radians(cameraPitch));
cameraFront = glm::normalize(front);
}