Skip to content

Instantly share code, notes, and snippets.

@Wunkolo
Last active June 13, 2021 05:36
Show Gist options
  • Save Wunkolo/20e19c0bc137867fe864 to your computer and use it in GitHub Desktop.
Save Wunkolo/20e19c0bc137867fe864 to your computer and use it in GitHub Desktop.
Just a fun ASCII raytracer I made one day.
#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;
}
//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;
}
@Wunkolo
Copy link
Author

Wunkolo commented Jun 13, 2021

sir in the secound it is terminated by <3ds.h>
plz check this

That is the point. The second one is called ASCIIRayTrace3DS.cpp because it is intended to run on the Nintendo 3DS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment