|
|
|
Difficulty of this task depends on features you want -- a super simple [flat shaded](flat_shading.md) (no textures, no smooth [shading](shading.md) renderer is relatively easy to make, especially if you don't need movable camera, can afford to use [floating point](float.md) etc. See the details of [3D rendering](3d_rendering.md), especially how the GPU pipelines work, and try to imitate them in software. The core of these renderers is the **[triangle](triangle.md) [rasterization](rasterization.md)** algorithm which, if you want, can be very simple -- even a naive one will give workable results -- or pretty complex and advanced, using various optimizations and things such as the [top-left rule](top_left_rule.md) to guarantee no holes and overlaps of triangles. Remember this function will likely be the performance [bottleneck](bottleneck.md) of your renderer so you want to put effort into [optimizing](optimization.md) it to achieve good [FPS](fps.md). Once you have triangle rasterization, you can draw 3D models which consist of vertices (points in 3D space) and triangles between these vertices (it's very simple to load simple 3D models e.g. from the [obj](obj.md) format) -- you simply project (using [perspective](perspective.md)) 3D position of each vertex to screen coordinates and draw triangles between these pixels with the rasterization algorithm. Here you need to also solve [visibility](visibility.md), i.e. possible overlap of triangles on the screen and correctly drawing those nearer the view in front of those that are further away -- a very simple solution is a [z buffer](z_buffer.md), but to save memory you can also e.g. [sort](sorting.md) the triangles by distance and draw them back-to-front ([painter's algorithm](painters_algorithm.md)). You may add a [scene](scene.md) data structure that can hold multiple models to be rendered. If you additionally want to have movable camera and models that can be transformed (moved, rotated, scaled, ...), you will additionally need to look into some [linear algebra](linear_algebra.md) and [transform matrices](transform_matrix.md) that allow to efficiently compute positions of vertices of a transformed model against a transformed camera -- you do this the same way as basically all other 3D engines (look up e.g. some [OpenGL](opengl.md) tutorials, see model/view/projection [matrices](matrix.md) etc.). If you also want texturing, the matters get again a bit more complicated, you need to compute [barycentric](barycentric.md) coordinates (special coordinates within a triangle) as you're rasterizing the triangle, and possibly apply [perspective correction](perspective_correction.md) (otherwise you'll be seeing distortions). You then map the barycentrics of each rasterized pixel to [UV](uv.md) (texturing) coordinates which you use to retrieve specific pixels from a texture. On top of all this you may start adding all the advanced features of typical engines such as [acceleration structures](acceleration_structure.md) that for example discard models that are completely out of view, [LOD](lod.mf), instancing, [MIP maps](mip_map.md) and so on.
|
|
|
|
Difficulty of this task depends on features you want -- a super simple [flat shaded](flat_shading.md) (no textures, no smooth [shading](shading.md)) renderer is relatively easy to make, especially if you don't need movable camera, can afford to use [floating point](float.md) etc. See the details of [3D rendering](3d_rendering.md), especially how the GPU pipelines work, and try to imitate them in software. The core of these renderers is the **[triangle](triangle.md) [rasterization](rasterization.md)** algorithm which, if you want, can be very simple -- even a naive one will give workable results -- or pretty complex and advanced, using various optimizations and things such as the [top-left rule](top_left_rule.md) to guarantee no holes and overlaps of triangles. Remember this function will likely be the performance [bottleneck](bottleneck.md) of your renderer so you want to put effort into [optimizing](optimization.md) it to achieve good [FPS](fps.md). Once you have triangle rasterization, you can draw 3D models which consist of vertices (points in 3D space) and triangles between these vertices (it's very simple to load simple 3D models e.g. from the [obj](obj.md) format) -- you simply project (using [perspective](perspective.md)) 3D position of each vertex to screen coordinates and draw triangles between these pixels with the rasterization algorithm. Here you need to also solve [visibility](visibility.md), i.e. possible overlap of triangles on the screen and correctly drawing those nearer the view in front of those that are further away -- a very simple solution is a [z buffer](z_buffer.md), but to save memory you can also e.g. [sort](sorting.md) the triangles by distance and draw them back-to-front ([painter's algorithm](painters_algorithm.md)). You may add a [scene](scene.md) data structure that can hold multiple models to be rendered. If you additionally want to have movable camera and models that can be transformed (moved, rotated, scaled, ...), you will additionally need to look into some [linear algebra](linear_algebra.md) and [transform matrices](transform_matrix.md) that allow to efficiently compute positions of vertices of a transformed model against a transformed camera -- you do this the same way as basically all other 3D engines (look up e.g. some [OpenGL](opengl.md) tutorials, see model/view/projection [matrices](matrix.md) etc.). If you also want texturing, the matters get again a bit more complicated, you need to compute [barycentric](barycentric.md) coordinates (special coordinates within a triangle) as you're rasterizing the triangle, and possibly apply [perspective correction](perspective_correction.md) (otherwise you'll be seeing distortions). You then map the barycentrics of each rasterized pixel to [UV](uv.md) (texturing) coordinates which you use to retrieve specific pixels from a texture. On top of all this you may start adding all the advanced features of typical engines such as [acceleration structures](acceleration_structure.md) that for example discard models that are completely out of view, [LOD](lod.mf), instancing, [MIP maps](mip_map.md) and so on.
|