#3 Draw a triangle
This commit is contained in:
parent
1df89e00e4
commit
1fcce95e9d
5
IPixelShader.cs
Normal file
5
IPixelShader.cs
Normal file
@ -0,0 +1,5 @@
|
||||
public interface IPixelShader
|
||||
{
|
||||
|
||||
float3 GetColor(float3 barycentric, int screenX, int screenY, int frame);
|
||||
}
|
||||
14
MathHelpers.cs
Normal file
14
MathHelpers.cs
Normal file
@ -0,0 +1,14 @@
|
||||
public class MathHelpers
|
||||
{
|
||||
public static float Edge(float2 a, float2 b, float2 c)
|
||||
{
|
||||
return (c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x);
|
||||
}
|
||||
|
||||
public static float2 NDCToScreen(float2 ndc, int width, int height)
|
||||
{
|
||||
float x = (ndc.x + 1f) * 0.5f * width;
|
||||
float y = (1f - (ndc.y + 1f) * 0.5f) * height; // flip Y
|
||||
return new float2(x, y);
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,11 @@
|
||||
|
||||
public static float3 operator -(float3 a, float3 b) => new float3(a.x - b.x, a.y - b.y, a.z - b.y);
|
||||
|
||||
public static float3 operator *(float scalar, float3 vec) => new float3(vec.x * scalar, vec.y * scalar, vec.z * scalar);
|
||||
|
||||
public static float3 operator +(float3 a, float3 b) => new float3(a.x + b.x, a.y + b.y, a.z + b.z);
|
||||
|
||||
|
||||
public static float3 cross(float3 a, float3 b) => new float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
||||
|
||||
public static float dot(float3 a, float3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
|
||||
@ -6,7 +6,7 @@ Writing a simple 3D software renderer from scratch to understand the math behind
|
||||
## 🎯 Milestones
|
||||
- [X] Setup Raylib C# project
|
||||
- [X] Draw an image from a byte array
|
||||
- [ ] Draw a triangle
|
||||
- [X] Draw a triangle
|
||||
- [ ] Render Lots of triangles
|
||||
- [ ] OBJ Loader
|
||||
- [ ] Ortho Projection
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Numerics;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Raylib_cs;
|
||||
|
||||
@ -13,6 +14,8 @@ public class RenderDevice
|
||||
private Texture2D renderTarget;
|
||||
private IntPtr pixelPtr;
|
||||
private byte[] pixelData;
|
||||
|
||||
private int frame = 1;
|
||||
public RenderDevice(int width, int height, int screenWidth, int screenHeight)
|
||||
{
|
||||
this.width = width;
|
||||
@ -38,17 +41,69 @@ public class RenderDevice
|
||||
}
|
||||
|
||||
Raylib.DrawTexturePro(renderTarget, new Rectangle(0,0, width, height), new Rectangle(0, 0, screenWidth, screenHeight), Vector2.Zero, 0f, Color.White);
|
||||
|
||||
frame++;
|
||||
}
|
||||
|
||||
public void SetPixel(int x, int y, float3 color)
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height) return;
|
||||
int idx = (y * width + x) * 4;
|
||||
pixelData[idx] = (byte)color.r;
|
||||
pixelData[idx + 1] = (byte)color.g;
|
||||
pixelData[idx + 2] = (byte)color.b;
|
||||
pixelData[idx] = (byte)(Math.Clamp(color.r * 255, 0, 255));
|
||||
pixelData[idx + 1] = (byte)(Math.Clamp(color.g * 255, 0, 255));
|
||||
pixelData[idx + 2] = (byte)(Math.Clamp(color.b * 255, 0, 255));
|
||||
pixelData[idx + 3] = 0xFF;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = 0; i < pixelData.Length; i++) pixelData[i] = 0;
|
||||
}
|
||||
|
||||
public void DrawTriangle(float2 v0, float2 v1, float2 v2, IPixelShader pixelShader)
|
||||
{
|
||||
v0 = MathHelpers.NDCToScreen(v0, width, height);
|
||||
v1 = MathHelpers.NDCToScreen(v1, width, height);
|
||||
v2 = MathHelpers.NDCToScreen(v2, width, height);
|
||||
|
||||
Debug.WriteLine(v0.x);
|
||||
|
||||
int minX = (int)MathF.Floor(MathF.Min(v0.x, MathF.Min(v1.x, v2.x)));
|
||||
int maxX = (int)MathF.Ceiling(MathF.Max(v0.x, MathF.Max(v1.x, v2.x)));
|
||||
int minY = (int)MathF.Floor(MathF.Min(v0.y, MathF.Min(v1.y, v2.y)));
|
||||
int maxY = (int)MathF.Ceiling(MathF.Max(v0.y, MathF.Max(v1.y, v2.y)));
|
||||
|
||||
float area = MathHelpers.Edge(v0, v1, v2);
|
||||
if (area == 0f) return; // Degenerate
|
||||
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
{
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
float2 p = new float2(x + 0.5f, y + 0.5f);
|
||||
|
||||
float w0 = MathHelpers.Edge(v1, v2, p);
|
||||
float w1 = MathHelpers.Edge(v2, v0, p);
|
||||
float w2 = MathHelpers.Edge(v0, v1, p);
|
||||
|
||||
|
||||
|
||||
if (w0 <= 0 && w1 <= 0 && w2 <= 0)
|
||||
{
|
||||
|
||||
w0 /= area;
|
||||
w1 /= area;
|
||||
w2 /= area;
|
||||
float3 bary = new float3(w0, w1, w2);
|
||||
|
||||
// Inside triangle
|
||||
SetPixel(x, y, pixelShader.GetColor(bary, x, y, frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void cleanup()
|
||||
{
|
||||
Raylib.UnloadTexture(renderTarget);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Raylib_cs;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Raylib_cs;
|
||||
|
||||
class SoftwareRasterizer
|
||||
{
|
||||
@ -16,27 +17,66 @@ class SoftwareRasterizer
|
||||
sr.start();
|
||||
}
|
||||
|
||||
struct BouncingPoint()
|
||||
{
|
||||
float posX, posY;
|
||||
float velX, velY;
|
||||
|
||||
|
||||
public float2 pos => new(posX, posY);
|
||||
|
||||
public BouncingPoint(float posX, float posY, float velX, float velY) : this()
|
||||
{
|
||||
this.posX = posX;
|
||||
this.posY = posY;
|
||||
this.velX = velX;
|
||||
this.velY = velY;
|
||||
}
|
||||
public void Update()
|
||||
{
|
||||
posX += velX;
|
||||
posY += velY;
|
||||
|
||||
if (posX >= 1.0 || posX <= -1.0f)
|
||||
{
|
||||
velX = -velX;
|
||||
}
|
||||
|
||||
if (posY >= 1.0 || posY <= -1.0f)
|
||||
{
|
||||
velY = -velY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
|
||||
Raylib.InitWindow(screenWidth, screenHeight, "Software Rasterizer");
|
||||
Raylib.SetTargetFPS(0);
|
||||
Raylib.SetTargetFPS(60);
|
||||
|
||||
renderer = new RenderDevice(screenWidth / scale, screenHeight / scale, screenWidth, screenHeight);
|
||||
|
||||
VertexColorShader shader = new VertexColorShader();
|
||||
|
||||
int frameCounter = 0;
|
||||
|
||||
Random rand = new Random();
|
||||
|
||||
BouncingPoint v0 = new BouncingPoint( 0.0f, 0.8f, 0.01f, 0.01f); // Top middle
|
||||
BouncingPoint v1 = new BouncingPoint( 0.8f, -0.8f, 0.01f, -0.01f); // Bottom right
|
||||
BouncingPoint v2 = new BouncingPoint(-0.8f, -0.8f, -0.01f, 0.01f); // Bottom left
|
||||
|
||||
while (!Raylib.WindowShouldClose())
|
||||
{
|
||||
for (int i = 0; i < renderer.height; i++)
|
||||
{
|
||||
for (int j = 0; j < renderer.width; j++)
|
||||
{
|
||||
renderer.SetPixel(j, i, new float3((j / (float)(renderer.width)) * 255, (i / (float)(renderer.height)) * 255, rand.Next(1,256)));
|
||||
}
|
||||
}
|
||||
|
||||
renderer.Clear();
|
||||
|
||||
v0.Update();
|
||||
v1.Update();
|
||||
v2.Update();
|
||||
|
||||
renderer.DrawTriangle(v0.pos, v1.pos, v2.pos, shader);
|
||||
// Render
|
||||
Raylib.BeginDrawing();
|
||||
Raylib.ClearBackground(Color.Black);
|
||||
@ -52,5 +92,9 @@ class SoftwareRasterizer
|
||||
renderer.cleanup();
|
||||
Raylib.CloseWindow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
27
VertexColorShader.cs
Normal file
27
VertexColorShader.cs
Normal file
@ -0,0 +1,27 @@
|
||||
public class VertexColorShader : IPixelShader
|
||||
{
|
||||
|
||||
float3[] vertexColors = new[]
|
||||
{
|
||||
new float3(1.0f, 0.0f, 0.0f),
|
||||
new float3(0.0f, 1.0f, 0.0f),
|
||||
new float3(0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
public float3 GetColor(float3 barycentric, int screenX, int screenY, int frame)
|
||||
{
|
||||
float amount1 = MathF.Cos(frame * 0.02f) * 0.5f + 0.5f;
|
||||
float amount2 = MathF.Sin(frame * 0.02f) * 0.5f + 0.5f;
|
||||
float3 c1 = new float3(vertexColors[0].x * amount1, vertexColors[0].y * amount1, vertexColors[0].z * amount1);
|
||||
float3 c2 = new float3(vertexColors[1].x * amount2, vertexColors[1].y * amount2, vertexColors[1].z * amount2);
|
||||
|
||||
float3 color = barycentric.x * c1 +
|
||||
barycentric.y * c2 +
|
||||
barycentric.z * vertexColors[2];
|
||||
|
||||
// if((int)(screenX + MathF.Cos(frame * 0.02f) * 30) % 5 == 0 && (int)(screenY + MathF.Sin(frame * 0.02f) * 30) % 5 == 0) color += new float3(0.0f, 0.25f, 0.0f);
|
||||
if((int)(screenX) % 5 == 0 && (int)(screenY) % 5 == 0) color += new float3(0.0f, 0.25f, 0.0f);
|
||||
|
||||
|
||||
return color;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user