上次是对于如何进行简单的二维颜色渲染做了一个初步入门,本次内容是对于光线追踪的第一次初步尝试。 在本次视频中,使用了glm开源图形库进行vector3的使用和简单成像。
关键程序如下:
// Renderer.h
#include "Walnut/Random.h"
#include <memory>
#include <glm/glm.hpp>
class Renderer
{
public:
Renderer() = default;
void OnResize(uint32_t width, uint32_t height);
void Render();
std::shared_ptr<Walnut::Image> GetFinalImage() const { return m_FinalImage; }
private:
uint32_t PerPixel(glm::vec2 coord);
private:
std::shared_ptr<Walnut::Image> m_FinalImage;
uint32_t* m_ImageData = nullptr;
};
// Renderer.cpp
#include "Renderer.h"
void Renderer::OnResize(uint32_t width, uint32_t height)
{
if (m_FinalImage)
{
// No resize necessary
if (m_FinalImage->GetWidth() == width && m_FinalImage->GetHeight() == height)
{
return;
}
m_FinalImage->Resize(width, height);
}
else
{
m_FinalImage = std::make_shared<Walnut::Image>(width, height, Walnut::ImageFormat::RGBA);
}
delete[] m_ImageData;
m_ImageData = new uint32_t[width * height];
}
void Renderer::Render()
{
// render every pixel
for (uint32_t y = 0; y < m_FinalImage->GetHeight(); ++y)
{
for (uint32_t x = 0; x < m_FinalImage->GetWidth(); ++x)
{
glm::vec2 coord = { (float)x / (float)m_FinalImage->GetWidth(), (float)y / (float)m_FinalImage->GetHeight() };
coord = coord * 2.0f - 1.0f; // -1 -> 1
m_ImageData[x + y * m_FinalImage->GetWidth()] = PerPixel(coord);
}
}
m_FinalImage->SetData(m_ImageData);
}
uint32_t Renderer::PerPixel(glm::vec2 coord)
{
uint8_t r = (uint8_t)(coord.x * 255.0f);
uint8_t g = (uint8_t)(coord.y * 255.0f);
glm::vec3 rayOrigin(0.0f, 0.0f, 2.0f);
glm::vec3 rayDirection(coord.x, coord.y, -1.0f);
float radius = 0.5f;
// rayDirection = glm::normalize(rayDirection);
// (bx^2 + by^2)t^2 + (2(axbx + ayby))t + (ax^2 + ay^2 - r^2) = 0
// where
// a = ray origin
// b = ray direction
// r = radius
// t = hit distance
//float a = coord.x * coord.x + coord.y * coord.y + coord.z * coord.z;
float a = glm::dot(rayDirection, rayDirection);
float b = 2.0f * glm::dot(rayOrigin, rayDirection);
float c = glm::dot(rayOrigin, rayOrigin) - radius * radius;
// Quaddratic forumula discriminant:
// b^2 - 4ac
float discriminant = b * b - 4.0f * a * c;
if (discriminant >= 0.0f)
{
return 0xffff00ff;
}
return 0xff000000;
}
主程序调用如下:
virtual void OnUIRender() override
{
ImGui::Begin("Setting");
ImGui::Text("Last render: %.3fms", m_LastRenderTime);
if (ImGui::Button("Render"))
{
Render();
}
ImGui::End();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("Viewport");
m_ViewportWidth = ImGui::GetContentRegionAvail().x;
m_ViewportHeight = ImGui::GetContentRegionAvail().y;
auto image = m_Renderer.GetFinalImage();
//ImGui::ShowDemoWindow();
if(m_Renderer.GetFinalImage())
ImGui::Image(image->GetDescriptorSet(), { (float)image->GetWidth(), (float)image->GetHeight() },
ImVec2(0, 1), ImVec2(1,0) );
ImGui::End();
ImGui::PopStyleVar();
Render();
}
void Render()
{
Timer timer;
m_Renderer.OnResize(m_ViewportWidth, m_ViewportHeight);
m_Renderer.Render();
m_LastRenderTime = timer.ElapsedMillis();
}
最终会显示一个粉色的球体:
虽然看起来就是画了一个二维圆,但是貌似是由光线追踪的数学原理成像得来的,等有时间再研究一下,等到下次再做总结吧。