Guten Abend,
ich bin vor Kurzem auf die Idee gekommen eine sehr abgespeckte Spielekonsole zu bauen Um zu gucken was für Mikrocontroller ich brauche habe ich recherchiert und ein Testprojekt angefangen. Ziel ist es erstmal ein großes 3D Modell einzulesen und mittels eines Viewports auf eine Bitmap zu zeichnen. Funktioniert auch alles soweit. Mit höchster Optimierungsstufe auf einem AMD Athlon II X2 250 3,00Ghz Prozessor schaffe ich 100 mal rendern in 3,39s (shadeless)
und 4s (flat shading).
Das ist natürlich echt langsam und auf einem Mikrocontroller mit ein paar MHz unmöglich. Leider kann ich deswegen auch nichts parallelisieren. Hier ist die eigentliche Render Klasse:
Spoiler anzeigen
Das ist die viewport<>::vec_to_raster :
Wobei transform und projection Matrizen sind. Ich weiß man wird etwas erschlagen, habe aber alles Notwendige kommentiert Kann man an der
Grüße
ich bin vor Kurzem auf die Idee gekommen eine sehr abgespeckte Spielekonsole zu bauen Um zu gucken was für Mikrocontroller ich brauche habe ich recherchiert und ein Testprojekt angefangen. Ziel ist es erstmal ein großes 3D Modell einzulesen und mittels eines Viewports auf eine Bitmap zu zeichnen. Funktioniert auch alles soweit. Mit höchster Optimierungsstufe auf einem AMD Athlon II X2 250 3,00Ghz Prozessor schaffe ich 100 mal rendern in 3,39s (shadeless)
und 4s (flat shading).
Das ist natürlich echt langsam und auf einem Mikrocontroller mit ein paar MHz unmöglich. Leider kann ich deswegen auch nichts parallelisieren. Hier ist die eigentliche Render Klasse:
C-Quellcode
- template <typename camera>
- class renderer
- {
- typedef typename camera::type t;
- public:
- renderer(const camera& vp, shade_type mode = shade_type::shadeless)
- {
- z_buffer = new t[camera::width*camera::height];
- fill(z_buffer, z_buffer + (camera::width*camera::height), vp.get_far_plane()); // Set Z buffer to far plane
- shading_mode = mode;
- }
- ~renderer()
- {
- delete[] z_buffer;
- }
- void reset(const camera& vp)
- {
- fill(z_buffer, z_buffer + (camera::width*camera::height), vp.get_far_plane()); // Set Z buffer to far plane
- }
- void render_frame(const camera& vp, const triangle_mesh& mesh, bitmap<512, 512>& frame_buffer, bitmap<1024, 512>& texture)
- {
- for (uint32_t i = 0; i < mesh.faces.size(); ++i)
- {
- const vec3<t>& v0 = mesh.vertices[mesh.faces[i].x]; // Vertices world
- const vec3<t>& v1 = mesh.vertices[mesh.faces[i].y];
- const vec3<t>& v2 = mesh.vertices[mesh.faces[i].z];
- vec3<t> v0_raster = vp.vec_to_raster(v0); // Vertices raster
- vec3<t> v1_raster = vp.vec_to_raster(v1);
- vec3<t> v2_raster = vp.vec_to_raster(v2);
- // X, Y max/min values of the triangle. Might be < 0!
- t xmin = min(v0_raster.x, min(v1_raster.x, v2_raster.x));
- t xmax = max(v0_raster.x, max(v1_raster.x, v2_raster.x));
- t ymin = min(v0_raster.y, min(v1_raster.y, v2_raster.y));
- t ymax = max(v0_raster.y, max(v1_raster.y, v2_raster.y));
- // Out of screen
- if (xmin > vp.width - 1 || xmax < 0 || ymin > vp.height || ymax < 0)
- continue;
- v0_raster.z = 1 / v0_raster.z; // Invert z, after that it can be multiplied instead of division
- v1_raster.z = 1 / v1_raster.z;
- v2_raster.z = 1 / v2_raster.z;
- vec3<t> c0 = mesh.colours[mesh.faces[i].x]; // Vertex colours
- vec3<t> c1 = mesh.colours[mesh.faces[i].y];
- vec3<t> c2 = mesh.colours[mesh.faces[i].z];
- vec2<t> st0 = mesh.uvs[mesh.faces[i].x]; // Texture coordinates
- vec2<t> st1 = mesh.uvs[mesh.faces[i].y];
- vec2<t> st2 = mesh.uvs[mesh.faces[i].z];
- vec3<t> n0 = mesh.normals[mesh.faces[i].x]; // Normals
- vec3<t> n1 = mesh.normals[mesh.faces[i].y];
- vec3<t> n2 = mesh.normals[mesh.faces[i].z];
- c0 = c0 * v0_raster.z; // divide by z
- c1 = c1 * v1_raster.z;
- c2 = c2 * v2_raster.z;
- st0 = st0 * v0_raster.z;
- st1 = st1 * v1_raster.z;
- st2 = st2 * v2_raster.z;
- if (shading_mode == shade_type::smooth)
- {
- n0 = n0 * v0_raster.z;
- n1 = n1 * v1_raster.z;
- n2 = n2 * v2_raster.z;
- }
- // x/y start/end values | Either min or 0 / max or (Width|Height)
- uint32_t x0 = max(int32_t(0), static_cast<int32_t>(floor(xmin)));
- uint32_t x1 = min(int32_t(vp.width - 1), static_cast<int32_t>(floor(xmax)));
- uint32_t y0 = max(int32_t(0), static_cast<int32_t>(floor(ymin)));
- uint32_t y1 = min(int32_t(vp.height - 1), static_cast<int32_t>(floor(ymax)));
- t area = edge(v0_raster, v1_raster, v2_raster); // Edgefunction
- vec3<t> first_pixel(x0 + t(0.5), y0 + t(0.5), 0); // Centre of the first pixel
- // Precompute first values + x/y step of the edge function
- t w0c = edge(v1_raster, v2_raster, first_pixel);
- t w0_step_x = (v2_raster.y - v1_raster.y);
- t w0_step_y = -(v2_raster.x - v1_raster.x);
- w0c -= w0_step_x;
- t w0_start = w0c;
- t w1c = edge(v2_raster, v0_raster, first_pixel);
- t w1_step_x = (v0_raster.y - v2_raster.y);
- t w1_step_y = -(v0_raster.x - v2_raster.x);
- w1c -= w1_step_x;
- t w1_start = w1c;
- t w2c = edge(v0_raster, v1_raster, first_pixel);
- t w2_step_x = (v1_raster.y - v0_raster.y);
- t w2_step_y = -(v1_raster.x - v0_raster.x);
- w2c -= w2_step_x;
- t w2_start = w2c;
- for (uint32_t y = y0; y <= y1; ++y) // Loop through all Pixels of the triangle's bounding box {(x0, y0), (x1, y1)}
- {
- for (uint32_t x = x0; x <= x1; ++x)
- {
- w0c += w0_step_x; // Increment by x step
- w1c += w1_step_x;
- w2c += w2_step_x;
- t w0 = w0c; // Barycentric coordinates
- t w1 = w1c;
- t w2 = w2c;
- if (!(w0 >= 0 && w1 >= 0 && w2 >= 0))
- continue; // Pixel is not within the triangle
- w0 /= area;
- w1 /= area;
- w2 /= area;
- t z = 1 / (v0_raster.z * w0 + v1_raster.z * w1 + v2_raster.z * w2);
- if (z >= z_buffer[y*camera::width + x]) // Not visible
- continue;
- z_buffer[y*camera::width + x] = z; // Set z buffer
- vec3<t> v_colour = c0 * w0 + c1 * w1 + c2 * w2; // Interpolate colours and stuff
- vec2<t> v_st = st0 * w0 + st1 * w1 + st2 * w2;
- vec3<t> v_normal;
- if (shading_mode == shade_type::smooth)
- v_normal = n0 * w0 + n1 * w1 + n2 * w2;
- v_colour = v_colour*z; // Perspective correct
- v_st = v_st*z;
- t dot_view = t(1);
- if (shading_mode != shade_type::shadeless)
- {
- v_normal = v_normal*z;
- vec3<t> v0_cam = vp.get_transform_c()*v0;
- vec3<t> v1_cam = vp.get_transform_c()*v1;
- vec3<t> v2_cam = vp.get_transform_c()*v2;
- t px = (v0_cam.x / -v0_cam.z) * w0 + (v1_cam.x / -v1_cam.z) * w1 + (v2_cam.x / v2_cam.z) * w2;
- t py = (v0_cam.y / -v0_cam.z) * w0 + (v1_cam.y / -v1_cam.z) * w1 + (v2_cam.y / v2_cam.z) * w2;
- vec3<t> pt(px*z, py*z, -z); // Point in camera space
- vec3f n;
- if(shading_mode == shade_type::smooth)
- n = v_normal;
- else
- n = (v1_cam - v0_cam).cross(v2_cam - v0_cam);
- n.normalise();
- vec3f view_direction = -pt;
- view_direction.normalise();
- dot_view = max(t(0), n.dot(view_direction));
- }
- auto final_colour = v_colour * texture.get(v_st.x, v_st.y) * dot_view * 255.0; // Colour*Texture*Normal
- frame_buffer.put(x, y, static_cast<unsigned char>(final_colour.x), static_cast<unsigned char>(final_colour.y), static_cast<unsigned char>(final_colour.z));
- }
- w0_start += w0_step_y; // Increment by y step
- w0c = w0_start;
- w1_start += w1_step_y;
- w1c = w1_start;
- w2_start += w2_step_y;
- w2c = w2_start;
- }
- }
- }
- private:
- t* z_buffer;
- shade_type shading_mode;
- };
Das ist die viewport<>::vec_to_raster :
Wobei transform und projection Matrizen sind. Ich weiß man wird etwas erschlagen, habe aber alles Notwendige kommentiert Kann man an der
render_frame()
Methode noch etwas optimieren?Grüße