{ "cells": [ { "cell_type": "markdown", "id": "f65f4fda", "metadata": {}, "source": [ "(framebufferlabel)=\n", "# Framebuffer" ] }, { "cell_type": "markdown", "id": "852486fe", "metadata": {}, "source": [ "We are taking a detour, to learn more about framebuffers. This section\n", "is written as a jupyter notebook,\n", "which is an interactive document with executable code, which can\n", "output values and images.\n", "\n", "You can run this either in spyder or in jupyter. If using spyder,\n", "open \"src/modelviewprojection/notebooksrc/framebuffer.py\", if\n", "using jupyter notebook, using jupyter, open \"notebook/framebuffer.ipynb\"\n", "\n", "We will be using a fake \"framebuffer\", just a python class to\n", "represent a framebuffer, and see how it works." ] }, { "cell_type": "code", "execution_count": null, "id": "03f67b05", "metadata": {}, "outputs": [], "source": [ "# Copyright (c) 2025 William Emerison Six\n", "#\n", "# This program is free software; you can redistribute it and/or\n", "# modify it under the terms of the GNU General Public License\n", "# as published by the Free Software Foundation; either version 2\n", "# of the License, or (at your option) any later version.\n", "#\n", "# This program is distributed in the hope that it will be useful,\n", "# but WITHOUT ANY WARRANTY; without even the implied warranty of\n", "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n", "# GNU General Public License for more details.\n", "#\n", "# You should have received a copy of the GNU General Public License\n", "# along with this program; if not, write to the Free Software\n", "# Foundation, Inc., 59 Temple Place - Suite 330,\n", "# Boston, MA 02111-1307, USA.\n", "\n", "\n", "import warnings\n", "\n", "import modelviewprojection.mathutils2d as mu2d" ] }, { "cell_type": "markdown", "id": "422fbe67", "metadata": { "lines_to_next_cell": 0 }, "source": [ "The module below is our software implementation of a\n", "framebuffer, we will use the name \"sr\" for\n", "\"software rendering\":" ] }, { "cell_type": "code", "execution_count": null, "id": "5ef37d1c", "metadata": { "lines_to_next_cell": 0 }, "outputs": [], "source": [ "import modelviewprojection.softwarerendering as sr\n", "\n", "# turn warnings into exceptions\n", "warnings.filterwarnings(\"error\", category=RuntimeWarning)" ] }, { "cell_type": "markdown", "id": "e0106e58", "metadata": {}, "source": [ "Make a framebuffer, which is just a rectangular region of\n", "values. We use keyword arguments to\n", "specify that the framebuffer should have a width\n", "of 100, and a height of 100" ] }, { "cell_type": "code", "execution_count": null, "id": "bf8cc30b", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Show initial random framebuffer\n", "fake_fb: sr.FrameBuffer = sr.FrameBuffer(width=100, height=100)\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "a267d867", "metadata": {}, "source": [ "Well, that looks bad, as there is a bunch of random values\n", "for colors in the framebuffer.\n", "\n", "Draw a triangle in this framebuffer, using screenspace coordinates" ] }, { "cell_type": "code", "execution_count": null, "id": "3c28926d", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Example: draw a white triangle\n", "fake_fb.draw_filled_triangle(\n", " mu2d.Vector2D(50, 50),\n", " mu2d.Vector2D(50, 70),\n", " mu2d.Vector2D(70, 70),\n", " color=sr.WHITE,\n", ")\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "7ac4caea", "metadata": {}, "source": [ "Whoops! That looks like trash. We should change the background to\n", "have all one color. That's what clear_framebuffer does" ] }, { "cell_type": "code", "execution_count": null, "id": "d3f7264a", "metadata": {}, "outputs": [], "source": [ "# Clear to red and show\n", "fake_fb.clear_color = sr.RED\n", "fake_fb.clear_framebuffer()\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "cfa3c846", "metadata": {}, "source": [ "I don't really want to draw on Red, let's switch it to black" ] }, { "cell_type": "code", "execution_count": null, "id": "9188c215", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Clear to black and show\n", "fake_fb.clear_color = sr.BLACK\n", "fake_fb.clear_framebuffer()\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "370eb083", "metadata": {}, "source": [ "Let's clear again for no reason, and draw a white triangle in screenspace." ] }, { "cell_type": "code", "execution_count": null, "id": "32cfc4c8", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Example: draw a white triangle\n", "fake_fb.clear_framebuffer()\n", "fake_fb.draw_filled_triangle(\n", " mu2d.Vector2D(50, 50),\n", " mu2d.Vector2D(50, 70),\n", " mu2d.Vector2D(70, 70),\n", " color=sr.WHITE,\n", ")\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "bdc8b890", "metadata": {}, "source": [ "Without clearing (as we want to keep the white triangle, let's\n", "draw a red triangle at a different location in screenspace" ] }, { "cell_type": "code", "execution_count": null, "id": "33578c46", "metadata": {}, "outputs": [], "source": [ "\n", "# draw a red triangle\n", "fake_fb.draw_filled_triangle(\n", " mu2d.Vector2D(50, 50),\n", " mu2d.Vector2D(30, 50),\n", " mu2d.Vector2D(30, 30),\n", " color=sr.RED,\n", ")\n", "fake_fb.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "4262dc7f", "metadata": { "lines_to_next_cell": 0 }, "source": [ "Make a larger framebuffer" ] }, { "cell_type": "code", "execution_count": null, "id": "1a663cc2", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Show initial random framebuffer\n", "fake_fb400: sr.FrameBuffer = sr.FrameBuffer(width=400, height=400)\n", "fake_fb400.show_framebuffer()" ] }, { "cell_type": "code", "execution_count": null, "id": "a88a9cbe", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "# Clear to black and show\n", "fake_fb400.clear_framebuffer()\n", "fake_fb400.show_framebuffer()" ] }, { "cell_type": "code", "execution_count": null, "id": "b96e6204", "metadata": {}, "outputs": [], "source": [ "# Example: draw a white triangle\n", "fake_fb400.clear_framebuffer()\n", "fake_fb400.draw_filled_triangle(\n", " mu2d.Vector2D(50, 50),\n", " mu2d.Vector2D(50, 70),\n", " mu2d.Vector2D(70, 70),\n", " color=sr.WHITE,\n", ")\n", "fake_fb400.show_framebuffer()\n", "\n", "fake_fb400.draw_filled_triangle(\n", " mu2d.Vector2D(50, 50),\n", " mu2d.Vector2D(30, 50),\n", " mu2d.Vector2D(30, 30),\n", " color=sr.RED,\n", ")\n", "fake_fb400.show_framebuffer()" ] }, { "cell_type": "markdown", "id": "e2db63b2", "metadata": {}, "source": [ "It makes sense that the triangles are in the lower left, OpenGL\n", "screenspace starts with 0,0 in the bottom left; the upper\n", "right is (width, height)" ] }, { "cell_type": "code", "execution_count": null, "id": "e9f8e3e4", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }