Last active
June 13, 2021 05:36
-
-
Save Wunkolo/20e19c0bc137867fe864 to your computer and use it in GitHub Desktop.
Just a fun ASCII raytracer I made one day.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <math.h> | |
#include <string> | |
#include <initializer_list> | |
#include <thread> | |
#include <chrono> | |
//#define V_SSE | |
#if R_FIXED | |
typedef uint32_t Real | |
#else | |
typedef float Real; | |
#endif | |
#ifdef V_SSE | |
#include <pmmintrin.h> | |
#endif | |
struct Vector3 | |
{ | |
union | |
{ | |
#ifdef V_SSE | |
__m128 _v128; | |
#elif V_FIXED | |
#else | |
Real v[3]; | |
#endif | |
}; | |
Vector3() | |
{ | |
#ifdef V_SSE | |
_v128 = _mm_setzero_ps(); | |
#elif V_FIXED | |
#else | |
v[0] = v[1] = v[2] = 0; | |
#endif | |
} | |
#ifdef V_SSE | |
Vector3(__m128 v) : | |
_v128(v) | |
{ | |
} | |
#endif | |
Vector3(Real x, Real y = (Real)0, Real z = (Real)0) | |
{ | |
#ifdef V_SSE | |
_v128 = _mm_setr_ps(x, y, z, 0); | |
#elif V_FIXED | |
#else | |
v[0] = x; | |
v[1] = y; | |
v[2] = z; | |
#endif | |
} | |
Real Length() | |
{ | |
#ifdef V_SSE | |
return (Real)_mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(_v128, _v128, 0x71))); | |
#elif V_FIXED | |
#else | |
return (Real)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | |
#endif | |
} | |
void Normalize() | |
{ | |
#ifdef V_SSE | |
_v128 = _mm_mul_ps(_v128, _mm_rsqrt_ps(_mm_dp_ps(_v128, _v128, 0x7f))); | |
#elif V_FIXED | |
#else | |
Real len = Length(); | |
v[0] /= len; | |
v[1] /= len; | |
v[2] /= len; | |
#endif | |
} | |
Real dot(const Vector3& other) | |
{ | |
#ifdef V_SSE | |
return _mm_cvtss_f32(_mm_dp_ps(_v128, other._v128, 0x71)); | |
#elif V_FIXED | |
#else | |
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2]; | |
#endif | |
} | |
Vector3 cross(const Vector3& other) | |
{ | |
#ifdef V_SSE | |
__m128 t1 = _mm_shuffle_ps(_v128, _v128, 0xc9); | |
__m128 t2 = _mm_shuffle_ps(_v128, _v128, 0xd2); | |
__m128 t3 = _mm_shuffle_ps(other._v128, other._v128, 0xd2); | |
__m128 t4 = _mm_shuffle_ps(other._v128, other._v128, 0xc9); | |
__m128 t5 = _mm_mul_ps(t1, t3); | |
__m128 t6 = _mm_mul_ps(t2, t4); | |
return Vector3(_mm_sub_ps(t5, t6)); | |
#elif V_FIXED | |
#else | |
return Vector3 | |
( | |
v[1] * other.v[2] - v[2] * other.v[1], | |
v[2] * other.v[0] - v[0] * other.v[2], | |
v[0] * other.v[1] - v[1] * other.v[0] | |
); | |
#endif | |
}; | |
Vector3 operator +(const Vector3& other) const | |
{ | |
#ifdef V_SSE | |
return Vector3(_mm_add_ps(_v128, other._v128)); | |
#elif V_FIXED | |
#else | |
return Vector3(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]); | |
#endif | |
} | |
Vector3 operator -(const Vector3& other) const | |
{ | |
#ifdef V_SSE | |
return Vector3(_mm_sub_ps(_v128, other._v128)); | |
#elif V_FIXED | |
#else | |
return Vector3(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); | |
#endif | |
} | |
Vector3 operator *(const Real& other) const | |
{ | |
#ifdef V_SSE | |
return Vector3(_mm_mul_ps(_v128, _mm_set1_ps(other))); | |
#elif V_FIXED | |
#else | |
return Vector3(v[0] * other, v[1] * other, v[2] * other); | |
#endif | |
} | |
}; | |
struct Ray | |
{ | |
Vector3 Position; | |
Vector3 Direction; | |
}; | |
struct Intersection | |
{ | |
Vector3 Position; | |
Vector3 Normal; | |
}; | |
struct Sphere | |
{ | |
Vector3 Position; | |
Real Radius; | |
bool Intersect(const Ray& ray, Intersection * intersect) | |
{ | |
Vector3 v = Position - ray.Position; | |
Real b = v.dot(ray.Direction); | |
Real disc = b * b - v.dot(v) + Radius * Radius; | |
if( disc < 0 ) | |
{ | |
return false; | |
} | |
if( intersect != nullptr ) | |
{ | |
Real d = sqrt(disc); | |
//Two possible solutions: pick closest to camera | |
Real t1 = b - d; | |
Real t2 = b + d; | |
intersect->Position = ray.Position + ray.Direction * (t1 > 0 ? t1 : t2); | |
intersect->Normal = intersect->Position - Position; | |
intersect->Normal.Normalize(); | |
} | |
return true; | |
} | |
}; | |
#define WIDTH (79) | |
#define HEIGHT (37) | |
#define ASPECT ((WIDTH/(Real)HEIGHT)*(Real)0.5) | |
#define FOVFactor (Real)0.5773502691896257645091487805019574556476017512701268 // 60 degrees FOV(tan((pi/3)/2)) | |
const char shades[] = ".:*oe&#%@"; | |
int main() | |
{ | |
Vector3 EyePos(0, 2.5, -3.75); | |
Vector3 Lookat(0, 0, 0); | |
Vector3 EyeDir = Lookat - EyePos; | |
EyeDir.Normalize(); | |
Vector3 EyeRight = EyeDir.cross(Vector3(0, -1, 0)); | |
Vector3 EyeUp = EyeRight.cross(EyeDir); | |
EyeUp.Normalize(); | |
EyeRight.Normalize(); | |
Vector3 LightPos(0, 15, -5); | |
Sphere ball; | |
ball.Position = Vector3(0, 0, 0); | |
ball.Radius = 1; | |
Intersection intersect; | |
Real Time = 0; | |
Real TimeStep = 1 / (Real)12; | |
std::string Buff; | |
do | |
{ | |
for( int32_t y = 0; y < HEIGHT; y++ ) | |
{ | |
for( int32_t x = 0; x < WIDTH; x++ ) | |
{ | |
Ray TestRay; | |
TestRay.Position = EyePos; | |
Vector3 PixelPoint = EyeRight * FOVFactor * ASPECT * Real((x / (Real)WIDTH) - (Real)0.5) + | |
EyeUp * FOVFactor * Real((y / (Real)HEIGHT) - (Real)0.5) + | |
EyePos + EyeDir; | |
TestRay.Direction = PixelPoint - EyePos; | |
TestRay.Direction.Normalize(); | |
if( ball.Intersect(TestRay, &intersect) ) | |
{ | |
Vector3 LightDir = LightPos - intersect.Position; | |
LightDir.Normalize(); | |
Real Diffuse = intersect.Normal.dot(LightDir); | |
if( Diffuse >= 0 ) | |
{ | |
Buff += (shades[(uint32_t)((Diffuse*Diffuse*Diffuse)*(sizeof(shades) - 1))]); | |
} | |
else | |
{ | |
Buff += '+'; | |
} | |
} | |
else | |
{ | |
Buff += ' '; | |
} | |
} | |
Buff += '\n'; | |
} | |
printf("%s", Buff.c_str()); | |
Buff.clear(); | |
printf("%f", Time); | |
Time += TimeStep; | |
LightPos = Vector3((Real)cos(Time*(3.14 * 4)) * 15, 10, (Real)sin(Time*(3.14 * 4)) * 15); | |
std::this_thread::sleep_for(std::chrono::milliseconds(int(TimeStep * 1000))); | |
} while( true ); | |
system("pause"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//3ds version | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <math.h> | |
#include <string.h> | |
#include <3ds.h> | |
typedef float Real; | |
struct Vector3 | |
{ | |
Real v[3]; | |
Vector3() | |
{ | |
v[0] = v[1] = v[2] = 0; | |
} | |
Vector3(Real x, Real y = (Real)0, Real z = (Real)0) | |
{ | |
v[0] = x; | |
v[1] = y; | |
v[2] = z; | |
} | |
Real Length() | |
{ | |
return (Real)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); | |
} | |
void Normalize() | |
{ | |
Real len = Length(); | |
v[0] /= len; | |
v[1] /= len; | |
v[2] /= len; | |
} | |
Real dot(const Vector3& other) | |
{ | |
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2]; | |
} | |
Vector3 cross(const Vector3& other) | |
{ | |
return Vector3 | |
( | |
v[1] * other.v[2] - v[2] * other.v[1], | |
v[2] * other.v[0] - v[0] * other.v[2], | |
v[0] * other.v[1] - v[1] * other.v[0] | |
); | |
}; | |
Vector3 operator +(const Vector3& other) const | |
{ | |
return Vector3(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]); | |
} | |
Vector3 operator -(const Vector3& other) const | |
{ | |
return Vector3(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); | |
} | |
Vector3 operator *(const Real& other) const | |
{ | |
return Vector3(v[0] * other, v[1] * other, v[2] * other); | |
} | |
}; | |
struct Ray | |
{ | |
Vector3 Position; | |
Vector3 Direction; | |
}; | |
struct Intersection | |
{ | |
Vector3 Position; | |
Vector3 Normal; | |
}; | |
struct Sphere | |
{ | |
Vector3 Position; | |
Real Radius; | |
bool Intersect(const Ray& ray, Intersection * intersect) | |
{ | |
Vector3 v = Position - ray.Position; | |
Real b = v.dot(ray.Direction); | |
Real disc = b * b - v.dot(v) + Radius * Radius; | |
if( disc < 0 ) | |
{ | |
return false; | |
} | |
if( intersect != nullptr ) | |
{ | |
Real d = sqrt(disc); | |
//Two possible solutions: pick closest to camera | |
Real t1 = b - d; | |
Real t2 = b + d; | |
intersect->Position = ray.Position + ray.Direction * (t1 > 0 ? t1 : t2); | |
intersect->Normal = intersect->Position - Position; | |
intersect->Normal.Normalize(); | |
} | |
return true; | |
} | |
}; | |
#define WIDTH (400) | |
#define HEIGHT (240) | |
#define ASPECT ((WIDTH/(Real)HEIGHT)) | |
#define FOVFactor (Real)0.5773502691896257645091487805019574556476017512701268 // 60 degrees FOV(tan((pi/3)/2)) | |
inline void PutPixel(int x, int y, char r, char g, char b, u8* screen) | |
{ | |
screen += (((x)* 240) + ((240 - 1) - (y))) * 3; | |
screen[0] = b; | |
screen[1] = g; | |
screen[2] = r; | |
} | |
const char shades[] = ".:*oe&#%@"; | |
int main() | |
{ | |
// Initializations | |
srvInit(); // services | |
aptInit(); // applets | |
hidInit(NULL); // input | |
gfxInit(GSP_RGBA8_OES, GSP_RGB565_OES, 0); // graphics | |
gfxSet3D(true); // stereoscopy (true == on / false == off) | |
u32 kDown; // keys down | |
u32 kHeld; // keys pressed | |
u32 kUp; // keys up | |
u8* fbTopLeft; // top left screen's framebuffer | |
u8* fbTopRight; // top right screen's framebuffer | |
u8* fbBottom; // bottom screen's framebuffer | |
float EYEOFF = 5; | |
Vector3 EyePos(0, 5, 5); | |
Vector3 Lookat(0, 0, 0); | |
Vector3 EyeDir = Lookat - EyePos; | |
EyeDir.Normalize(); | |
Vector3 EyeRight = EyeDir.cross(Vector3(0, -1, 0)); | |
Vector3 EyeUp = EyeRight.cross(EyeDir); | |
EyeUp.Normalize(); | |
EyeRight.Normalize(); | |
Vector3 LightPos(0, 15, -5); | |
Sphere ball; | |
ball.Position = Vector3(0, 0, 0); | |
ball.Radius = 1; | |
Intersection intersect; | |
// Main loop | |
while( aptMainLoop() ) | |
{ | |
gspWaitForVBlank(); | |
hidScanInput(); | |
kDown = hidKeysDown(); | |
kHeld = hidKeysHeld(); | |
kUp = hidKeysUp(); | |
if( kDown & KEY_START ) | |
{ | |
break; | |
} | |
if( kHeld & KEY_DUP ) | |
{ | |
EyePos.v[2] += 0.5f; | |
} | |
if( kHeld & KEY_DUP ) | |
{ | |
EyePos.v[2] -= 0.5f; | |
} | |
if( kHeld & KEY_DLEFT ) | |
{ | |
EyePos.v[0] += 0.5f; | |
} | |
if( kHeld & KEY_DRIGHT ) | |
{ | |
EyePos.v[0] -= 0.5f; | |
} | |
if( kDown & KEY_B ) | |
{ | |
EYEOFF -= 0.5f; | |
} | |
if( kDown & KEY_A ) | |
{ | |
EYEOFF += 0.5f; | |
} | |
// Reset framebuffers | |
fbTopLeft = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); | |
fbTopRight = gfxGetFramebuffer(GFX_TOP, GFX_RIGHT, NULL, NULL); | |
//fbBottom = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL); | |
//memset(fbTopLeft, 0, 240 * 400 * 3); | |
//memset(fbTopRight, 0, 240 * 400 * 3); | |
//memset(fbBottom, 0, 240 * 320 * 3); | |
consoleInit(GFX_BOTTOM, NULL); | |
EyeDir = Lookat - EyePos; | |
EyeDir.Normalize(); | |
EyeRight = EyeDir.cross(Vector3(0, -1, 0)); | |
EyeUp = EyeRight.cross(EyeDir); | |
/** Your code starts here **/ | |
printf("Rendering left eye...\n"); | |
for( unsigned int y = 0; y < HEIGHT; y++ ) | |
{ | |
for( unsigned int x = 0; x < WIDTH; x++ ) | |
{ | |
Ray TestRay; | |
TestRay.Position = EyePos; | |
Vector3 PixelPoint = EyeRight * FOVFactor * ASPECT * Real(((x + EYEOFF) / (Real)WIDTH) - (Real)0.5) + | |
EyeUp * FOVFactor * Real((y / (Real)HEIGHT) - (Real)0.5) + | |
EyePos + EyeDir; | |
TestRay.Direction = PixelPoint - EyePos; | |
TestRay.Direction.Normalize(); | |
if( ball.Intersect(TestRay, &intersect) ) | |
{ | |
Vector3 LightDir = LightPos - intersect.Position; | |
LightDir.Normalize(); | |
Real Diffuse = intersect.Normal.dot(LightDir); | |
if( Diffuse >= 0 ) | |
{ | |
Diffuse *= Diffuse; | |
PutPixel(x, y, u8(255 * Diffuse), u8(255 * Diffuse), u8(255 * Diffuse), fbTopRight); | |
} | |
else | |
{ | |
PutPixel(x, y, 0, 0, 0, fbTopRight); | |
} | |
} | |
else | |
{ | |
PutPixel(x, y, 16, 16, 16, fbTopRight); | |
} | |
} | |
} | |
printf("Rendering right eye..."); | |
for( unsigned int y = 0; y < HEIGHT; y++ ) | |
{ | |
for( unsigned int x = 0; x < WIDTH; x++ ) | |
{ | |
Ray TestRay; | |
TestRay.Position = EyePos; | |
Vector3 PixelPoint = EyeRight * FOVFactor * ASPECT * Real(((x - EYEOFF) / (Real)WIDTH) - (Real)0.5) + | |
EyeUp * FOVFactor * Real((y / (Real)HEIGHT) - (Real)0.5) + | |
EyePos + EyeDir; | |
TestRay.Direction = PixelPoint - EyePos; | |
TestRay.Direction.Normalize(); | |
if( ball.Intersect(TestRay, &intersect) ) | |
{ | |
Vector3 LightDir = LightPos - intersect.Position; | |
LightDir.Normalize(); | |
Real Diffuse = intersect.Normal.dot(LightDir); | |
Diffuse *= Diffuse; | |
Diffuse *= Diffuse; | |
if( Diffuse >= 0 ) | |
{ | |
PutPixel(x, y, u8(255 * Diffuse), u8(255 * Diffuse), u8(255 * Diffuse), fbTopLeft); | |
} | |
else | |
{ | |
PutPixel(x, y, 0, 0, 0, fbTopLeft); | |
} | |
} | |
else | |
{ | |
PutPixel(x, y, 16, 16, 16, fbTopLeft); | |
} | |
} | |
} | |
/** End of your code **/ | |
printf("Done!\n"); | |
// Flush and swap framebuffers | |
gfxFlushBuffers(); | |
gfxSwapBuffers(); | |
} | |
// Exit | |
gfxExit(); | |
hidExit(); | |
aptExit(); | |
srvExit(); | |
// Return to hbmenu | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
That is the point. The second one is called
ASCIIRayTrace3DS.cpp
because it is intended to run on the Nintendo 3DS.