Files
picovga-RGsB/examples/vga_raytrace/src/render.cpp
2024-12-30 11:26:26 -05:00

304 lines
6.8 KiB
C++

// ****************************************************************************
//
// Ray-trace rendering
//
// ****************************************************************************
#include "render.h"
#include "main.h"
#include "vector.h"
#include "sphere.h"
// list of spheres
Sphere Spheres[] = {
// position, radius, color, reflection, transparency (transparency not realized yet)
Sphere(V3(-1.5,-0.4,1), 1, V3(1,1,0), 0.5, 0), // yellow sphere
Sphere(V3(0,0.4,0), 1, V3(1,0,0), 0.5, 0), // red sphere
Sphere(V3(2,-0.1,-6), 2, V3(0,0,(real)0.4), (real)0.7, 0), // blue sphere
Sphere(V3(3,0.2,1), 1.5, V3(0,(real)0.5,0), (real)0.6, 0), // green sphere
};
#define OBJNUM count_of(Spheres)
V3 Camera(0, 0, 10); // camera position
V3 Light(-6, 4, 10); // light position
real Ambient = (real)0.3; // intensity of ambient light
V3 BackCol((real)0.4, (real)0.6, 1); // background color in horizon
#define TOOFAR 1e10
#define FLOORY -2 // floor Y position
#define DEPTHMAX 6 // max. depth of trace
// trace ray
void Trace(V3* rgb, V3 &orig, V3 &dir, int depth, Sphere* disable)
{
// find nearest intersection
real t1best = TOOFAR;
Sphere* sbest = NULL;
real t1, t2;
uint i;
for (i = 0; i < OBJNUM; i++)
{
if (&Spheres[i] == disable) continue;
t1 = TOOFAR;
t2 = TOOFAR;
if (Spheres[i].Intersect(orig, dir, &t1, &t2))
{
if (t1 < 0) t1 = t2;
if (t1 < t1best)
{
t1best = t1;
sbest = &Spheres[i];
}
}
}
// if object not found, return sky color or continue with floor plane
V3 col, pos, norm;
real refl;
if (sbest == NULL)
{
// sky (black in top direction)
real k = dir.y;
if (dir.y >= 0)
{
k *= 2.5;
if (k > 1) k = 1;
*rgb = BackCol*(1-k);
return;
}
// floor - substitute with plane parameters
t1best = (orig.y - FLOORY) / dir.y;
pos = orig - (dir*t1best);
norm.Set(0,1,0);
col = ((int)(ceil(pos.x) + ceil(pos.z)) & 1) ? V3(1,1,1) : V3(1,0.25,0.25);
refl = (real)0.4;
}
else
{
// coordinate and normal in intersection point
pos = orig + (dir*t1best);
norm = pos - sbest->pos;
norm.Norm();
col = sbest->col;
refl = sbest->ref;
}
// if normal and ray direction are not opposited, we are inside sphere, then reverse normal
if (dir.Dot(norm) > 0)
{
norm.Inv();
}
// vector to light
V3 light = Light - pos;
light.Norm();
// check if point is in shadow
real intens = 1;
for (i = 0; i < OBJNUM; i++)
{
if (sbest == &Spheres[i]) continue;
if (Spheres[i].Intersect(pos, light, &t1, &t2))
{
intens = 0;
break;
}
}
// get diffusion color
intens = intens*norm.Dot(light)*(1-Ambient);
if (intens <= 0) intens = 0;
*rgb = col * (intens + Ambient);
// add reflection
if ((refl > 0) && (depth < DEPTHMAX))
{
// reflection vector
V3 rdir = dir - norm*2*dir.Dot(norm);
rdir.Norm();
// add reflection
V3 rgb2;
Trace(&rgb2, pos, rdir, depth + 1, sbest);
*rgb *= 1 - refl;
*rgb += rgb2 * refl;
}
}
// render image fast
void Render3DFast()
{
// local variables
int tmp;
int x, y; // current X and Y coordinates in bitmap
real xx, yy; // current X and Y coordinates in viewing plane
V3 rgbV; // result pixel color as vactor 0..1
V3 orig(0, 0, 10); // camera position
V3 dir; // ray direction
real fov = 45*PI/180; // field of view in degrees
real tfov = (real)tan(fov/2); // height/2 of viewing plane
real ar = WIDTH/(real)HEIGHT; // aspect ratio
u8* dst = &Box[0];
int red, green, blue;
// render picture
for (y = HEIGHT-2; y >= 0; y -= 2)
{
for (x = 0; x < WIDTH; x += 2)
{
// ray direction vector
xx = (real)(2*(x + 0.5)/WIDTH - 1) * tfov * ar;
yy = (real)(2*(y + 0.5)/HEIGHT - 1) * tfov;
dir.Set(xx, yy, -1);
dir.Norm();
// trace this ray
Trace(&rgbV, Camera, dir, 0, NULL);
// convert vector to RGB pixel
tmp = (int)(rgbV.x*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
red = tmp; // red
tmp = (int)(rgbV.y*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
green = tmp; // green
tmp = (int)(rgbV.z*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
blue = tmp; // blue
// dithering correction
int k = ((x ^ y) & 2) << 2;
// write RGB pixel
red += k + RandS8MinMax(-2, 3);
if (red > 255) red = 255;
if (red < 0) red = 0;
red &= 0xe0;
green += k + RandS8MinMax(-2, 3);
if (green > 255) green = 255;
if (green < 0) green = 0;
green &= 0xe0;
blue += (k<<1) + RandS8MinMax(-3, 4);
if (blue > 255) blue = 255;
if (blue < 0) blue = 0;
u8 b = (u8)(red | (green >> 3) | (blue >> 6));
dst[WIDTH] = b;
dst[WIDTH+1] = b;
*dst++ = b;
*dst++ = b;
}
dst += WIDTH;
}
}
// render image (useaa = use antialiasing)
void Render3D(Bool useaa)
{
// local variables
int tmp;
int x, y; // current X and Y coordinates in bitmap
real xx, yy; // current X and Y coordinates in viewing plane
V3 rgbV; // result pixel color as vactor 0..1
V3 orig(0, 0, 10); // camera position
V3 dir; // ray direction
real fov = 45*PI/180; // field of view in degrees
real tfov = (real)tan(fov/2); // height/2 of viewing plane
real ar = WIDTH/(real)HEIGHT; // aspect ratio
u8* dst = &Box[0];
int red, green, blue;
// render picture
for (y = HEIGHT-1; y >= 0; y--)
{
for (x = 0; x < WIDTH; x++)
{
// use antialiasing
if (useaa)
{
int ix, iy;
V3 rgbVtmp;
rgbV.Zero();
for (iy = 0; iy < 3; iy++)
{
for (ix = 0; ix < 3; ix++)
{
// ray direction vector
xx = (real)(2*(x + ix/(real)3 + 1/(real)6)/WIDTH - 1) * tfov * ar;
yy = (real)(2*(y + iy/(real)3 + 1/(real)6)/HEIGHT - 1) * tfov;
dir.Set(xx, yy, -1);
dir.Norm();
// trace this ray
Trace(&rgbVtmp, Camera, dir, 0, NULL);
rgbV += rgbVtmp;
}
}
rgbV *= (real)1/9;
}
// no antialiasing
else
{
// ray direction vector
xx = (real)(2*(x + 0.5)/WIDTH - 1) * tfov * ar;
yy = (real)(2*(y + 0.5)/HEIGHT - 1) * tfov;
dir.Set(xx, yy, -1);
dir.Norm();
// trace this ray
Trace(&rgbV, Camera, dir, 0, NULL);
}
// convert vector to RGB pixel
tmp = (int)(rgbV.x*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
red = tmp; // red
tmp = (int)(rgbV.y*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
green = tmp; // green
tmp = (int)(rgbV.z*256 + 0.5);
if (tmp < 0) tmp = 0;
if (tmp > 255) tmp = 255;
blue = tmp; // blue
// dithering correction
int k = ((x ^ y) & 3) << 3;
// write RGB pixel
red += k + RandS8MinMax(-3, 5);
if (red > 255) red = 255;
if (red < 0) red = 0;
red &= 0xe0;
green += k + RandS8MinMax(-3, 5);
if (green > 255) green = 255;
if (green < 0) green = 0;
green &= 0xe0;
blue += (k<<1) + RandS8MinMax(-6, 10);
if (blue > 255) blue = 255;
if (blue < 0) blue = 0;
*dst++ = (u8)(red | (green >> 3) | (blue >> 6));
}
}
}