Coding Mastery: Beyond Rendering
Coding Mastery
Beyond Rendering
Realizing the Science and Algorithms Behind Visual Representation
SUJAL CHOUDHARI
Notice
Copyright Notice
All rights reserved. No part of this book may be reproduced or
transmitted in any form, electronic or mechanical, including
photocopying, recording, or by any information storage and
retrieval system, without written permission from the
publisher, except for the inclusion of brief quotations in a
review.
Disclaimer
The information contained in this book is for informational
purposes only and is not intended to be a substitute for
professional advice. While all attempts have been made to
ensure the accuracy and completeness of the information
presented, the author and publisher assume no responsibility
for errors, omissions, or damages arising from the use of the
information contained herein.
Dedication
To all the dreamers who, like me, have been captivated by the
magic of game development, this book is dedicated to you. My
journey into programming began with a fascination for game
development and rendering, leading to the creation of my own
prototypes, Indie Engine and [Link]. These endeavors have
been the foundation of my deep dive into the world of game
engines, a passion that continues to fuel my exploration and
innovation. "Coding Mastery: Beyond Rendering" is a testament
to that journey, it touches the shallow yet important part of
game development, that is rendering and I hope it inspires you
to pursue your own creative visions in the realm of coding and
beyond.
Table of Contents
Notice .......................................................................................................... v
Copyright Notice ............................................................................... v
Disclaimer ............................................................................................ v
Preface ....................................................................................................... xi
Beyond Rendering ........................................................................... 1
Introduction to Rendering Engines ................................................. 2
What is a rendering engine? ......................................................... 2
What is the history of the rendering engine? ........................ 4
The Future of 3D Rendering: Transformations and
Trends for 2024 ................................................................................ 9
Setting Up the Engine ................................................................... 14
Building the Rendering Engine ...................................................... 22
Understanding the Main Loop .................................................. 22
Geometry Class ............................................................................... 27
Render for our Geometry ........................................................... 32
More with Geometry .................................................................... 35
Line...................................................................................................... 35
Cube .................................................................................................... 38
Sphere ................................................................................................ 43
Circle ................................................................................................... 52
Cylinder ............................................................................................. 58
Curves ................................................................................................ 66
- viii –
Lights .................................................................................................. 74
Visualizing Light in 3D Space .................................................... 77
Free Camera .................................................................................... 82
Hidden Surface Removal ............................................................ 92
Enhancements and Advanced Techniques................................ 96
Custom Save File ............................................................................ 96
Introduction to the Scene Class ............................................. 100
Keyframe-Based Animation .................................................... 104
Debug Editor Window ............................................................... 109
Application ..................................................................................... 117
Using it all together .................................................................... 125
Where can we take this from here? ........................................... 126
Advanced Lighting Models....................................................... 126
Shadow Mapping ......................................................................... 127
Physics Integration ..................................................................... 127
Particle Systems ........................................................................... 128
Animation System ....................................................................... 128
Procedural Generation .............................................................. 129
Virtual Reality (VR) Support ................................................... 129
Artificial Intelligence (AI) ........................................................ 129
Networked Multiplayer ............................................................. 130
Asset Pipeline................................................................................ 130
Post-Processing Effects ............................................................. 131
Custom Shader Support ............................................................ 131
Level Editor ................................................................................... 132
Optimizations................................................................................ 132
x................................................................................................. Coding Mastery: Rendering Realities
Cross-Platform Compatibility .................................................133
Documentation and Tutorials .................................................133
Community and Plugin System ..............................................134
Integration with External Tools .............................................134
AI-Driven Enhancements..........................................................135
Augmented Reality (AR) Support ..........................................135
Preface
Welcome back, brave traveler of the internet world! In
Rendering Realities: Exploring the Science and Algorithms
Behind Visual Representation, in Volume One, we took a
fundamental look at the core ideas and algorithms that power
rendering engines. We investigated the mathematics of
transformations, discovered the secrets of lighting and
shading, and dabbled in the intriguing worlds of curves,
surfaces, and animation.
In Beyond Rendering: Building Interactive and
Dynamic Worlds, we're ready to take the next exciting step:
putting theoretical knowledge into practical use. This volume
focuses on creating your own rendering engine, which is the
technology that brings virtual worlds to life.
Prepare to roll up your sleeves and delve into the code!
Chapter 2: Building the Rendering Engine will walk through
the process of establishing the engine's core structure and
implementing the foundational functionality covered in
Volume One. We'll turn ideas like rendering primitives,
performing transformations, and modeling lighting effects into
tangible code, cementing your understanding and giving you
the ability to view the world inside the computer.
- xi -
xii ............................................................................................. Coding Mastery: Rendering Realities
But the trip does not end there. In Chapter Three:
Enhancements and Advanced Techniques, we'll go further into
advanced rendering techniques like texture mapping and
sophisticated shading to improve the visual integrity of your
engine. We'll also cover real-time rendering, which is an
important idea for generating dynamic and immersive
experiences. Finally, in Chapter Four, we talk about what path
this engine can take in future.
Remember, a thousand lines of code start with a single
keystroke. With this book as your guide, you'll be well on your
way to becoming an expert virtual world creator. So, unleash
your imagination, accept the difficulties, and prepare to bring
your rendering fantasies to reality!
~ Sujal Choudhari
Beyond Rendering
-1-
2 ................................................................................................ Coding Mastery: Rendering Realities
CHAPTER ONE
Introduction to Rendering Engines
“
The cost of adding more realism is not very high. The cost
of adding more dynamics, more light sources, more
moving objects, more things under simulation - that's
what costs you
~ John Carmack
This is what Dion Moult has to say about rendering
engines and their history. Dion Moult is involved in
architecture, construction, and software development. And
has written various articles on these topics.
What is a rendering engine?
A rendering engine is a software component that
converts markup content (such as HTML, XML, and picture
files) into formatting information (such as CSS and XSL) and
then displays the formatted content on the screen. It reads the
code and generates a visual representation of the web page or
application. Rendering engines are used in web browsers to
correctly read and display websites for users. They play an
important role in deciding how a website will appear and
Sujal Choudhari............................................................................................................................................ 3
perform on various browsers and devices. Without rendering
engines, websites would be unable to display information
correctly and uniformly across all platforms. Different
browsers utilize different rendering engines, resulting in
variances in how a website looks to users. Aside from Web
browsers, your computer's operating system uses rendering
engines to show graphical user interfaces and other visual
components on the screen. These rendering engines guarantee
that material is shown accurately and effectively, resulting in a
smooth user experience. Every GUI device, including
smartphones, tablets, and smart TVs, relies on rendering
engines to parse code and show content correctly. In essence,
rendering engines are critical components that allow digital
interfaces to perform smoothly across a wide range of devices.
Aside from business material, game developers employ
rendering engines to produce immersive and visually
attractive images for gamers to enjoy. These engines play an
important role in optimizing performance and ensuring that
games operate smoothly across several platforms, thereby
improving the entire gaming experience. That is why we are
interested in this book: to understand more about the function
of rendering engines in game creation and how they help to
produce compelling and dynamic game experiences.
Understanding the technology underlying rendering engines
may help developers create visually appealing games that
attract players and keep them coming back for more.
4 ................................................................................................ Coding Mastery: Rendering Realities
What is the history of the rendering engine?
We employ basic renderings for diagrams, realistic
renderings for architectural visualizations, real-time
rendering for virtual reality prototypes, and point cloud
renderings for landscape and heritage scanning, among other
things. Because the last aim of archviz is to depict abstractly
while lowering the expenses of realistic prototypes, it's worth
looking toward the future of rendering for the industry. But
first, let me take a short look at rendering's history.
When the CG industry originally addressed how to
render an image on a scene, they were primarily concerned
with the hidden surface determination issue. In brief, when
you have a polygon, determine which surfaces are visible from
the camera's perspective. This mindset of thinking about how
to "color in an object" rather than how to imitate light resulted
in the creation of one of the first rendering techniques: flat
shading. We saw flat shading in our first volume of this book,
and it marked the beginning of a long journey towards more
realistic rendering techniques in computer graphics.
But if you want a quick recap of what flat shading is, in
flat shading, the rendering engine takes into account each
surface's normal to a light source. The surface was brighter
when it was face-on and darker when it was incidental. If the
passage between the normal surface and a light source
crossed another surface (i.e., was blocked), it was tinted black.
I've included an example of flat shading in the first image
Sujal Choudhari............................................................................................................................................ 5
below. This is essentially equivalent to a real physical event in
which the angle of incidence of a substance matters.
Figure 1: Examples of multiple shading techniques
This was quite simple and unrealistic. Flat shading was
eventually combined with specular shading, which was
essentially the same but had a significantly skewed angle of
the surface normal, as well as an extra parameter to control
the highlight falloff. Although this produced a beautiful
metallic shine (see monkey two in the image below), it was
just an artistic tactic and not based on a real-world incident.
Nonetheless, it continues to this day.
Shading techniques progressed when a Vietnamese
gentleman created the famous Phong shader. He had the idea
of interpolating vertex normals across vertices to produce a
color gradient across the face. This provided far more realistic
findings, but there were no matching real-world results.
The shading model's next modification occurred when
users saw completely black shadows. In actuality, global
illumination and ambient light ray bounces enable almost
anything to be effectively indirectly lit. There was no
6 ................................................................................................ Coding Mastery: Rendering Realities
computationally efficient solution to the problem at the time,
so an ambient light constant was added to boost global
lighting. This acted as a bridge into the present portrayal,
which concluded our historical lesson.
Figure 2: A render made in blender
The lesson of the story is that practically all shading
techniques had no real-world counterpart, and all future
advancements were based on a strategy that analyzed how to
color in a shape from the perspective of the shape itself. This
is essentially incorrect: in the real world, how an item appears
(at human sizes, not quantum mechanics scales) is determined
by light rays released by photon-emitting things (e.g., hot
objects) bouncing about and losing energy. Energy is
deposited and reflected on materials in a variety of ways,
Sujal Choudhari............................................................................................................................................ 7
depending on their micro surface defects and chemical
characteristics.
Fortunately, as these creative shaders were being
produced, physically-based "ray-tracing" rendering engines
were being created. These ray-tracers tracked photon beams
from and to cameras and light sources in the same manner
that they would in the real world. Back then, they were
interesting technological demonstrations, but they were
always too inefficient for any real job. However, theoretically,
we have demonstrated that if you apply enough
computational power to the issue, you may get photorealistic
results. Nowadays, of course, everyone is familiar with ray-
tracing, and it is almost the industry standard. I've included an
example of a Chrome Monkey reflecting the surroundings
below as a textbook illustration of what ray-tracing can do
that regular shaders cannot (at least not without hacks and
light maps). You may see another example of photo-realistic
rendering with cycles that I've created.
Ray-tracers are found in almost all widely used
rendering engines these days, including Blender Cycles, V-Ray,
Maxwell, Renderman, and Arnold. With their increasing speed,
they now integrate the CPU and GPU to provide rendering that
is nearly instantaneous. Three of the numerous advancements
that have made photo-realistic rendering simpler in recent
years include improvements to texture painters, physically
based rendering, and improved real-world scanners.
8 ................................................................................................ Coding Mastery: Rendering Realities
In essence, photorealism is getting easier and easier.
It's also fascinating to see that there is a modest tendency
toward increased scientific basis. These ray-tracers used to
have so many approximations—despite being partially
physically based—that real-world units were disregarded in
favor of artificial numbers.
This is significant because scanning real-world data at
ever-higher quality levels yields penultimate photorealism. No
matter how physically grounded they are, engines will
struggle to make use of this information if it is difficult to trace
it back to quantifiable scientific values and physical units.
Fortunately, things are really getting better. With
mainstream renderings, simple tasks like creating false-color
luminance pictures or using additional IES profiles in lighting
are becoming viable. The Disney shader's popularity is
gradually igniting interest in interoperability, and as with
penultimate photorealism, scientific values are the basis for
ultimate interoperability.
We know, at the very least, that the issue will
ultimately converge and leave us if we keep throwing
computers at it with a beautiful real image.
For my industry, architecture, this is wonderful news.
When it comes to renderings, architecture is no stranger to
smoke and mirrors; in fact, a tendency toward scientific
rendering makes it simpler to prototype at a low cost while
Sujal Choudhari............................................................................................................................................ 9
yet promising clients the same outcomes. While the
excitement is still high, let's play with photorealistic gaming
engines and virtual reality.
The Future of 3D Rendering: Transformations
and Trends for 2024
3D rendering has revolutionized the fields of
technology and design, providing unprecedented
opportunities for visualization and interaction in the digital
world. As we approach 2024, the landscape of 3D rendering is
on the brink of significant transformations that will redefine
our engagement with digital environments. This essay
explores the future of 3D rendering, focusing on key trends
such as real-time rendering, AI-powered rendering, cloud-
based solutions, sustainability, immersive experiences,
blockchain integration, GANs, cross-platform compatibility,
mobile rendering, personalization, and the ethical challenges
ahead.
Real-Time Rendering: A Game-Changer
One of the most influential trends in 3D rendering is
the emergence of real-time rendering. With advancements in
powerful GPUs and sophisticated algorithms, designers and
developers can now create and manipulate 3D visuals
instantaneously. This capability is particularly transformative
for the gaming industry, where responsive and immersive
environments are paramount. Real-time rendering enables
10 ............................................................................................. Coding Mastery: Rendering Realities
seamless interactions within digital worlds, enhancing the
user experience and setting new standards for visual fidelity
and interactivity.
AI-Powered Rendering for Unparalleled Realism
Artificial Intelligence (AI) is playing an increasingly
pivotal role in 3D rendering, driving the trend towards hyper-
realistic visuals. AI-powered rendering engines leverage
machine learning to analyze data and produce stunningly
realistic images. This technology finds applications across
various sectors, including architecture, film production, and
product design. By automating complex rendering tasks, AI
not only improves efficiency but also pushes the boundaries of
what is visually possible, creating more lifelike and convincing
digital representations.
Cloud-Based Rendering Solutions: Accessibility and Scalability
The rise of cloud-based rendering solutions is
democratizing access to high-quality 3D rendering resources.
By leveraging the cloud, businesses of all sizes, including small
enterprises and independent creators, can tap into powerful
rendering capabilities without the need for substantial
investments in hardware. Cloud-based solutions offer
scalability, allowing users to handle large rendering projects
efficiently. This accessibility fosters innovation and
competition, enabling a broader range of creators to produce
high-quality 3D content.
Sujal Choudhari......................................................................................................................................... 11
Sustainability in 3D Rendering
As environmental concerns become more pressing, the
3D rendering industry is focusing on sustainability. Energy-
efficient algorithms and hardware are being developed to
reduce the environmental footprint of rendering processes. By
optimizing resource usage and minimizing energy
consumption, the industry aims to make 3D rendering more
sustainable. This trend reflects a broader commitment to
environmental responsibility and aligns with global efforts to
combat climate change.
Immersive AR and VR Experiences
Augmented Reality (AR) and Virtual Reality (VR) are
becoming integral to the future of 3D rendering. These
technologies offer immersive experiences that blur the line
between the digital and physical worlds. In 2024, we can
expect to see further advancements in AR and VR rendering
capabilities, enhancing their application in fields such as
gaming, education, healthcare, and retail. The ability to create
and interact with highly realistic virtual environments opens
up new possibilities for innovation and engagement.
Blockchain Integration for Security and Transparency
Blockchain technology is being integrated into 3D
rendering processes to ensure security and transparency. By
leveraging blockchain, creators can protect intellectual
property, verify the authenticity of digital assets, and maintain
12 ............................................................................................. Coding Mastery: Rendering Realities
secure records of transactions. This integration addresses
concerns related to data security and ownership, providing a
robust framework for managing 3D rendering projects. As
blockchain technology continues to evolve, its role in
safeguarding digital content will become increasingly
important.
Generative Adversarial Networks (GANs) and Content Creation
Generative Adversarial Networks (GANs) are
revolutionizing content creation in 3D rendering. GANs
consist of two neural networks—a generator and a
discriminator—that work together to produce highly realistic
images. This technology is being used to automate the creation
of complex textures, models, and environments, significantly
reducing the time and effort required for content creation.
GANs enable the generation of diverse and detailed visuals,
enhancing the creative potential of 3D rendering.
Cross-Platform Compatibility and Mobile Rendering
The demand for cross-platform compatibility is driving
innovations in 3D rendering, ensuring that visuals can be
seamlessly integrated across various devices and operating
systems. Mobile rendering applications are also on the rise,
allowing users to create and view 3D content on smartphones
and tablets. This trend towards mobility and compatibility
enhances the accessibility and usability of 3D rendering tools,
Sujal Choudhari......................................................................................................................................... 13
catering to a broader audience and facilitating on-the-go
creativity.
Personalization and Customization of Visuals
Personalization is becoming a key trend in 3D
rendering, with a growing emphasis on creating customized
visuals tailored to individual preferences. Advances in
rendering technology enable the generation of personalized
content, from customized avatars in video games to bespoke
architectural designs. This trend towards personalization
enhances user engagement and satisfaction, providing unique
and meaningful experiences.
Ethical Considerations and Challenges
As 3D rendering technology advances, it brings with it
a range of ethical considerations and challenges. Issues such
as privacy concerns, intellectual property rights, and the
responsible use of AI-generated content must be addressed.
The industry needs to establish clear guidelines and standards
to ensure the ethical use of 3D rendering technologies.
Additionally, fostering a culture of responsibility and
transparency will be crucial in navigating the complex ethical
landscape of the future.
The future of 3D rendering is poised for remarkable
transformations, driven by trends such as real-time rendering,
AI-powered realism, cloud-based solutions, sustainability,
14 ............................................................................................. Coding Mastery: Rendering Realities
immersive experiences, blockchain integration, GANs, cross-
platform compatibility, mobile rendering, personalization, and
ethical considerations. These advancements will redefine how
we visualize and interact with the digital world, opening up
new possibilities for innovation and creativity. As we move
forward, it is essential to embrace these trends while
addressing the associated challenges to ensure a responsible
and sustainable future for 3D rendering.
Setting Up the Engine
Welcome to the exciting world of game engine
development! If you've ever dreamed of creating your own
games from scratch, you're in the right place. This book will
guide you through the process of building a game engine,
giving you the tools and knowledge to bring your game ideas
to life.
We'll be using a powerful combination of technologies:
OpenGL and Python. Don't worry if you're new to either of
these – we've chosen them specifically because they're
accessible and beginner-friendly.
Python is an excellent language for getting started with
programming. Its clean syntax and extensive libraries make it
easy to learn and use, allowing you to focus on the concepts
rather than getting bogged down in complex language rules.
Sujal Choudhari......................................................................................................................................... 15
For our graphics needs, we'll be using OpenGL,
specifically versions before 3.5. This choice might surprise
some, but there's a good reason for it. These earlier versions
of OpenGL are remarkably straightforward to use. You won't
need to deal with Vertex Array Objects (VAOs) or Vertex
Buffer Objects (VBOs), which can be confusing for beginners.
Instead, we'll use a syntax that's reminiscent of the classic
graphics.h library from Turbo C – simple, direct, and easy to
understand.
Another advantage of this approach is that we won't
need to handle complex matrix transformations ourselves.
OpenGL will take care of these for us, allowing us to focus on
the core concepts of game engine development.
Now, you might be wondering: what exactly is OpenGL?
Is it a library? A framework? Actually, it's neither – OpenGL is
a specification. It defines a standard interface for interacting
with graphics hardware, allowing developers to create high-
performance 2D and 3D graphics applications. Hardware
manufacturers then implement this specification in their
graphics drivers, ensuring that OpenGL code can run on a
wide variety of devices.
With our technologies chosen, let's dive in and set up a
basic window using Pygame, a set of Python modules designed
for writing video games. Here's a simple example to get us
started:
16 ............................................................................................. Coding Mastery: Rendering Realities
import pygame
from [Link] import *
from [Link] import *
from [Link] import *
def main():
[Link]()
display = (800, 600)
[Link].set_mode(display, DOUBLEBUF |
OPENGL)
while True:
for event in [Link]():
if [Link] == [Link]:
[Link]()
quit()
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT)
[Link]()
[Link](10)
if __name__ == "__main__":
main()
This code sets up a basic OpenGL window using
Pygame. It creates a window, enters a main loop where it
handles events (like closing the window), clears the screen,
and updates the display.
Sujal Choudhari......................................................................................................................................... 17
As we progress in building our game engine, we quickly
realize that while it's relatively easy to write initial code,
integrating it seamlessly with a larger codebase can be
challenging. This is where object-oriented programming
(OOP) principles come to our rescue. Let's examine how we
can refactor our window code to be more OOP-friendly,
making it easier to integrate with the rest of our engine as it
grows.
The code I have provided is a step in this direction. It
encapsulates the window functionality into a Window class,
which is a fundamental OOP concept. This approach offers
several advantages:
18 ............................................................................................. Coding Mastery: Rendering Realities
Window class:
import pygame
from [Link] import *
from [Link] import *
class Window:
def __init__(self, width, height, title):
[Link] = width
[Link] = height
[Link] = title
self.is_active = True
[Link]()
[Link].set_mode(([Link],
[Link]), [Link] | [Link])
[Link].set_caption([Link])
self.init_3d()
def init_3d(self):
glClearColor(1.0, 1.0, 1.0, 1.0)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK ,
GL_AMBIENT_AND_DIFFUSE)
glDepthFunc(GL_LEQUAL)
glMatrixMode(GL_PROJECTION)
gluPerspective(45.0, float([Link]) /
float([Link]), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
Sujal Choudhari......................................................................................................................................... 19
def update(self):
[Link]()
def clear(self):
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT)
def wait_for_close(self):
return self.is_active
def close(self):
self.is_active = False
1. Encapsulation: All the window-related properties and
methods are bundled together in one class. This makes
our code more organized and easier to manage.
2. Reusability: We can create multiple window instances
if needed, each with its own properties.
3. Maintainability: If we need to modify window behavior,
we only need to change it in one place.
4. Modularity: The Window class can be easily imported
and used in other parts of our engine.
Let's break down the key components of this OOP approach:
The __init__ method serves as our constructor,
initializing the window with specific dimensions and a title. It
also sets up the Pygame display and calls init_3d() to
configure OpenGL settings.
20 ............................................................................................. Coding Mastery: Rendering Realities
The init_3d() method encapsulates all the OpenGL
initialization code. This separation of concerns makes our
code more readable and maintainable.
Methods like update(), clear(), wait_for_close(),
and close() provide a clean interface for interacting with the
window. This abstraction shields the rest of our code from the
underlying Pygame and OpenGL implementations.
Here's how we might use this Window class in our main game
loop:
def main():
window = Window(800, 600, "My Game Engine")
while window.wait_for_close():
[Link]()
# Your game logic and rendering code here
[Link]()
for event in [Link]():
if [Link] == [Link]:
[Link]()
[Link]()
if __name__ == "__main__":
main()
Sujal Choudhari......................................................................................................................................... 21
This OOP approach allows us to interact with the
window through a clean, intuitive interface. As we continue to
develop our engine, we can easily extend this Window class or
create new classes that interact with it.
By structuring our code in this way, we're setting
ourselves up for success as our engine grows more complex.
We're creating a foundation that's not only functional but also
scalable and maintainable. This OOP mindset will serve us
well as we add more features and components to our game
engine, ensuring that our codebase remains organized and
manageable even as it expands.
22 ............................................................................................. Coding Mastery: Rendering Realities
CHAPTER TWO
Building the Rendering Engine
“
The art challenges the technology, and the technology
inspires the art
~ John Lasseter
Understanding the Main Loop
We have already seen the Window class, but we will
learn about it in simpler terms and without any OOP. The first
step in our trip is to create the required environment by
importing the modules. We'll need to import Pygame for
window management and event handling, as well as
PyOpenGL's OpenGL and GLU (OpenGL Utility) modules.
These imports provide us with the foundational components
for our application.
import pygame
from [Link] import *
from [Link] import *
from [Link] import *
Once the modules are imported, we must initialize
Pygame and create a display surface that supports OpenGL
Sujal Choudhari......................................................................................................................................... 23
rendering. This process involves calling [Link]() to
initialize all Pygame modules and then using
[Link].set_mode() to create the window. When
creating the display surface, we specify the desired resolution
and include special flags to enable double buffering and
OpenGL support. Double buffering is a technique used to
prevent screen flickering by drawing to a back buffer before
swapping it with the front buffer, ensuring smooth visual
transitions.
[Link]()
display = (800, 600)
[Link].set_mode(display, DOUBLEBUF | OPENGL)
gluPerspective(45, (display[0] / display[1]), 0.1,
50.0)
glTranslatef(0.0, 0.0, -5)
After setting up the Pygame window, we'll move on to
configure the OpenGL environment. This step is critical
because it determines how our 3D scene will be presented on
the 2D screen. We use the gluPerspective function to
configure the perspective projection, which includes the field
of vision, aspect ratio, and near and far clipping planes. These
parameters determine how much of the 3D world is seen and
how it is resized to fit the 2D screen. After establishing the
perspective, we usually use glTranslatef to shift the entire
scene back along the Z-axis, ensuring that our objects are
visible within the defined viewing frustum.
24 ............................................................................................. Coding Mastery: Rendering Realities
Now that our rendering environment is all up, we can
concentrate on developing the actual graphic we want to
display. For the sake of simplicity, let us create a basic triangle.
In OpenGL, forms are defined by describing their vertices in
3D space. To draw a triangle, use the glBegin and glEnd
functions to mark the start and end of our form description. In
between these calls, we use glVertex3f to specify the
coordinates of each vertex in the triangle. The act of defining
shapes with vertices is crucial to OpenGL programming and
serves as the foundation for generating more complicated
geometries.
def draw_triangle():
glBegin(GL_TRIANGLES)
glVertex3f(-1, -1, 0)
glVertex3f(1, -1, 0)
glVertex3f(0, 1, 0)
glEnd()
The heart of our application lies in the main game loop.
This loop is responsible for continuously updating and
rendering our scene, as well as handling any user input or
system events. The loop typically starts by processing events
using Pygame's event handling system. This allows us to
respond to user actions such as closing the window or
pressing keys. Within the loop, we clear the screen using
glClear to prepare for the next frame, call our drawing
function to render the triangle, and then use
Sujal Choudhari......................................................................................................................................... 25
[Link]() to update the display with the newly
rendered frame.
while True:
for event in [Link]():
if [Link] == [Link]:
[Link]()
quit()
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT)
draw_triangle()
[Link]()
[Link](10)
Color plays a crucial role in making our graphics
visually appealing. In OpenGL, we can set the color of our
shapes using the glColor3f function before drawing them.
This allows us to assign different colors to each vertex,
creating gradient effects, or to set a single color for the entire
shape. Experimenting with colors can dramatically change the
appearance of our simple graphic and is an excellent way to
begin exploring more advanced rendering techniques.
As we become more comfortable with basic shape
rendering, we can start to explore more complex graphics and
effects. One way to add visual interest is by introducing
rotation to our shapes. This can be achieved using OpenGL's
matrix manipulation functions such as glRotatef. By applying
rotations before drawing our shape, we can create animated
effects that bring our scene to life. Similarly, we can use
26 ............................................................................................. Coding Mastery: Rendering Realities
glScalef to change the size of our objects or glTranslatef to
move them around the scene.
Lighting is another fundamental concept in 3D graphics
that can significantly enhance the visual quality of our scene.
OpenGL provides a flexible lighting system that allows us to
define light sources and specify how they interact with objects
in the scene. By enabling lighting and configuring parameters
such as ambient, diffuse, and specular components, we can
create more realistic and visually appealing graphics.
However, it's important to note that lighting calculations can
be computationally expensive, so they should be used
judiciously, especially in real-time applications.
As our graphics become more complex, it's crucial to
optimize performance to ensure smooth rendering. One
effective technique is to use display lists, which allow us to
compile a set of OpenGL commands into a single callable unit.
This can significantly reduce the overhead of repeatedly
issuing the same drawing commands. Another optimization
technique is to use vertex buffer objects (VBOs), which store
vertex data in high-performance graphics memory, reducing
the amount of data that needs to be transferred between the
CPU and GPU.
Sujal Choudhari......................................................................................................................................... 27
Geometry Class
A fundamental component that enhances our graphics
engine by providing a structured approach to handling various
attributes of graphical objects.
The Geometry class serves as a cornerstone in our
graphics pipeline, encapsulating essential properties and
functionalities of geometric objects. By centralizing these
attributes and operations, we create a more modular and
maintainable codebase. This class is responsible for managing
an object's position, rotation, and color, as well as providing
methods to manipulate these properties.
Let's begin by examining the class initialization:
class Geometry:
def __init__(
self,
position=(0.0, 0.0, 0.0),
rotation=(0.0, 0.0, 0.0),
color=(1.0, 1.0, 1.0, 1.0),
update_callback=None,
):
self.update_callback = update_callback
[Link] = [Link](position,
dtype=np.float32)
[Link] = [Link](rotation,
dtype=np.float32)
[Link] = [Link](color,
dtype=np.float32)
28 ............................................................................................. Coding Mastery: Rendering Realities
The constructor takes four parameters: position,
rotation, color, and an optional update_callback. The position
and rotation are 3D vectors representing the object's location
and orientation in 3D space, while the color is a 4D vector
representing RGBA values. These parameters are converted to
NumPy arrays for efficient computation. The update_callback
is a function that can be called whenever the object's
properties are updated, allowing for custom behavior.
The class provides several methods to manipulate the object's
properties:
def translate(self, x, y, z):
[Link] += [Link]([x, y, z])
def rotate(self, x, y, z):
[Link] += [Link]([x, y, z])
def change_color(self, r, g, b, a=1.0):
[Link] = [Link]([r, g, b, a])
def change_position(self, x, y, z):
[Link] = [Link]([x, y, z])
def change_rotation(self, x, y, z):
[Link] = [Link]([x, y, z])
self.recalculate_normals()
Sujal Choudhari......................................................................................................................................... 29
These methods allow for incremental changes
(translate, rotate) or direct setting (change_position,
change_rotation, change_color) of the object's properties. The
recalculate_normals method is called after changing rotation,
as it may affect the object's surface normals, which are crucial
for lighting calculations.
One of the key features of this class is its ability to apply
transformations to the OpenGL state:
def apply_transformations(self):
glTranslatef(*[Link])
glRotatef([Link][0], 1, 0, 0)
glRotatef([Link][1], 0, 1, 0)
glRotatef([Link][2], 0, 0, 1)
def apply_color(self):
glColor4f([Link][0], [Link][1],
[Link][2], [Link][3])
These methods use OpenGL functions to set the current
transformation matrix and color state. This abstraction
simplifies the rendering process, as these operations can be
applied with a single method call before drawing the object.
The Geometry class also includes rotation matrix calculation:
def get_rotation_matrix(self):
angle = [Link]([Link])
cos_a = [Link](angle)
sin_a = [Link](angle)
30 ............................................................................................. Coding Mastery: Rendering Realities
rx = [Link](
[
[1, 0, 0, 0],
[0, cos_a[0], -sin_a[0], 0],
[0, sin_a[0], cos_a[0], 0],
[0, 0, 0, 1],
]
)
ry = [Link](
[
[cos_a[1], 0, sin_a[1], 0],
[0, 1, 0, 0],
[-sin_a[1], 0, cos_a[1], 0],
[0, 0, 0, 1],
]
)
rz = [Link](
[
[cos_a[2], -sin_a[2], 0, 0],
[sin_a[2], cos_a[2], 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
]
)
rotation_matrix = [Link]([Link](rz, ry), rx)
return rotation_matrix
The get_rotation_matrix method calculates a 4x4
rotation matrix based on the object's current rotation. This
Sujal Choudhari......................................................................................................................................... 31
matrix can be used for more complex transformations or for
integrating with other 3D math libraries. Finally, the class
includes a render method, which serves as a template for
object-specific rendering logic:
def render(self, camera_direction):
self.apply_transformations()
self.apply_color()
# Add rendering logic here
pass
This method applies the necessary transformations and color
settings, then leaves space for object-specific rendering code
to be implemented in subclasses.
By implementing this Geometry class, we've created a
powerful foundation for our graphics engine. It provides a
clean interface for managing object properties, handles
complex mathematical operations, and integrates seamlessly
with OpenGL functions. This abstraction allows developers to
focus on higher-level aspects of their graphics applications,
such as scene composition and object behavior, without
getting bogged down in the details of transformation and state
management.
Moreover, this class structure facilitates easy extension
for more specific geometric shapes. For example, we could
create subclasses like Cube, Sphere, or Mesh that inherit from
Geometry and implement their own specific rendering logic
32 ............................................................................................. Coding Mastery: Rendering Realities
while leveraging the common functionality provided by the
base class.
The Geometry class represents a significant step forward in
the development of our graphics engine. It encapsulates
essential attributes and operations of 3D objects, provides
methods for efficient manipulation and rendering, and offers a
clear pathway for future extensions. By abstracting these
common elements, we've created a more robust and flexible
system for creating complex 3D graphics applications.
Render for our Geometry
Now, for our newly created geometry class, we need a
renderer to help us manage the Geometry class instances. The
Renderer class is the foundation of our graphics engine,
controlling the management and display of geometric objects
in our 3D scene. This class serves as a central hub, bridging
the gap between our application logic and the individual
geometry objects, optimizing the overall rendering process.
At the heart of the Renderer class lies a simple yet
effective data structure - a list to store all our geometry
objects. The class initializes this list in its constructor:
class Renderer:
def __init__(self):
[Link] = []
Sujal Choudhari......................................................................................................................................... 33
This straightforward initialization sets the stage for
managing multiple geometric entities within a single, cohesive
system. To populate this list with geometric objects, the
Renderer class provides an intuitive add_object method:
def add_object(self, obj):
[Link](obj)
This method allows for seamless integration of new
geometric shapes into our rendering pipeline. Each time we
create a new object in our application, we can effortlessly add
it to the renderer using this method. The renderer then takes
on the responsibility of updating and rendering this object
alongside all others in the scene.
The update method of the Renderer class plays a crucial role
in maintaining the dynamic nature of our 3D scene:
def update(self, delta_time):
for obj in [Link]:
[Link](delta_time)
This method iterates through all objects managed by
the renderer, invoking their individual update methods. The
delta_time parameter, representing the time elapsed since
the last frame, is passed to each object's update method. This
allows for smooth animations and physics calculations,
ensuring that object movements and transformations are
consistent regardless of frame rate fluctuations.
34 ............................................................................................. Coding Mastery: Rendering Realities
The render method stands as the core functionality of
our Renderer class:
def render(self):
for obj in [Link]:
obj.apply_color()
[Link]()
This technique controls the actual drawing of our
scene. It iterates across all managed objects, taking two
critical actions for each. First, it calls obj.apply_color(),
which sets the current color in the OpenGL state machine,
guaranteeing that each object is rendered in the correct color.
Then it calls [Link]() to activate the object's specific
rendering mechanism. This technique enables each geometric
object to define its own drawing procedure, while the
Renderer oversees the entire process.
The centralization of the rendering process in this class
offers multiple benefits. It imposes a distinct division of
concerns, with the Renderer handling the overall rendering
process and individual geometry objects focusing on their
own rendering logic. This layout facilitates scene management
by making it easy to add and delete objects from the scene.
Furthermore, by grouping all changes and rendering
operations together, we may increase speed over processing
each item separately.
To utilize this Renderer in an application, one would
typically instantiate it in the main loop, populate it with
Sujal Choudhari......................................................................................................................................... 35
geometry objects, and invoke its update and render methods
each frame. For instance:
renderer = Renderer()
triangle = Triangle(position=(0, 0, 0), color=(1, 0,
0, 1))
square = Square(position=(1, 1, 0), color=(0, 1, 0,
1))
renderer.add_object(triangle)
renderer.add_object(square)
while running:
delta_time = calculate_delta_time()
[Link](delta_time)
[Link]()
swap_buffers()
More with Geometry
Line
In the Line class, the render method is responsible for
drawing the line segment in a 3D space using OpenGL. This
process involves a series of steps and OpenGL commands that
work together to display the line on the screen. Here’s a
detailed explanation of how OpenGL handles this rendering
task.
36 ............................................................................................. Coding Mastery: Rendering Realities
The rendering process begins with the
self.apply_transformations() method, which is responsible
for preparing the geometric transformations that will be
applied to the line. This method ensures that any
transformations, such as translations, rotations, or scaling, are
applied to the line’s vertices before they are drawn. In
OpenGL, this step involves modifying the model-view matrix
to reflect the desired changes in the line’s position,
orientation, and size within the 3D environment. By applying
these transformations, the line can be positioned and oriented
correctly relative to the camera and the scene.
Line class
from [Link] import *
from [Link] import *
from [Link] import Geometry
class Line(Geometry):
def __init__(self, origin, destination,
update=None):
super().__init__(position=[0,0,0],
update_callback=update)
self.p1 = origin
self.p2 = destination
def render(self, camera_position):
self.apply_transformations()
self.apply_color()
glBegin(GL_LINES)
glVertex3f(self.p1[0], self.p1[1],
self.p1[2])
Sujal Choudhari......................................................................................................................................... 37
glVertex3f(self.p2[0], self.p2[1],
self.p2[2])
glEnd()
Following the application of transformations, the
self.apply_color() method is invoked to set the visual
properties of the line. This method specifies the line’s color by
setting the appropriate OpenGL color attributes. In OpenGL,
the color of the line is determined by commands that specify
the color components for rendering. This step ensures that the
line is drawn with the correct color when rendered on the
screen.
Figure 3: Line drawn with a debug GUI
38 ............................................................................................. Coding Mastery: Rendering Realities
Once the transformations and color settings are in
place, the actual drawing of the line is carried out through a
sequence of OpenGL commands. The glBegin(GL_LINES)
function is called to signal the start of the drawing process for
a line segment. This function sets the drawing mode to
GL_LINES, which tells OpenGL to interpret each pair of vertices
as the endpoints of a line. Inside the glBegin and glEnd block,
the glVertex3f functions are used to specify the coordinates
of the line’s endpoints. The glVertex3f commands define the
positions of the two points, self.p1 and self.p2, that make
up the line segment. Finally, the glEnd() function is called to
indicate the end of the line drawing commands and complete
the line segment’s rendering.
Through these steps, OpenGL processes the commands
to render a line segment defined by the two points in 3D
space. The use of glBegin(GL_LINES) and glEnd() frames the
line’s geometry, while the glVertex3f commands provide the
specific details of the line’s endpoints. The combined effect of
these commands, along with the transformations and color
settings, results in the visual representation of a line segment
on the screen.
Cube
The Cube class extends the Geometry class to define a
3D cube object that can be rendered using OpenGL. The class
specifies the structure of the cube through vertices, edges, and
Sujal Choudhari......................................................................................................................................... 39
surfaces, which are fundamental components for visualizing
the 3D shape.
Figure 4: Cube drawn with no shading, alongside a GUI
Cube class
from [Link] import *
from [Link] import Geometry
import numpy as np
class Cube(Geometry):
def __init__(self, position, size, update=None):
super().__init__(position=position,
update_callback=update)
[Link] = size
40 ............................................................................................. Coding Mastery: Rendering Realities
[Link] = [
(0, 0, 0),
(size[0], 0, 0),
(size[0], size[1], 0),
(0, size[1], 0),
(0, 0, size[2]),
(size[0], 0, size[2]),
(size[0], size[1], size[2]),
(0, size[1], size[2]),
]
[Link] = [
(0,1),(1,2),(2,3),(3,0), # Bottom edges
(4,5),(5,6),(6,7),(7,4), # Top edges
(0,4),(1,5),(2,6),(3, 7) # Vertical edges
]
[Link] = [
(0, 1, 2, 3), # Front face
(4, 5, 6, 7), # Back face
(0, 1, 5, 4), # Bottom face
(3, 2, 6, 7), # Top face
(0, 3, 7, 4), # Left face
(1, 2, 6, 5) # Right face
]
[Link] = [
(0, 0, -1), # Front face
(0, 0, 1), # Back face
(0, -1, 0), # Bottom face
(0, 1, 0), # Top face
(-1, 0, 0), # Left face
(1, 0, 0) # Right face
]
Sujal Choudhari......................................................................................................................................... 41
self._original_normals = list([Link])
def render(self, camera_pos):
glPushMatrix()
self.apply_transformations()
self.apply_color()
glBegin(GL_QUADS)
for i, surface in enumerate([Link]):
for vertex in surface:
v = [Link][vertex]
glVertex3fv(v)
glEnd()
glPopMatrix()
In the Cube class, the vertices, edges, and surfaces lists
play crucial roles in defining the geometric structure of the
cube. Here’s a detailed look at each component:
Vertices
The vertices list contains the corner points of the cube.
Each vertex is represented as a tuple of three coordinates (x, y,
z), which specifies its position in 3D space. The vertices list for
the cube is defined as follows:
[Link] = [
(0, 0, 0),
(size[0], 0, 0),
(size[0], size[1], 0),
(0, size[1], 0),
42 ............................................................................................. Coding Mastery: Rendering Realities
(0, 0, size[2]),
(size[0], 0, size[2]),
(size[0], size[1], size[2]),
(0, size[1], size[2]),
]
Here, size defines the dimensions of the cube, with
size[0], size[1], and size[2] representing the width,
height, and depth, respectively. The eight vertices define the
corners of the cube, starting from the bottom front-left corner
and moving through the other corners of the cube in a
sequence.
Edges
The edges list defines the connections between vertices
to form the edges of the cube. Each edge is specified as a pair
of indices referring to the vertices list:
[Link] = [
(0, 1), (1, 2), (2, 3), (3, 0), # Bottom edges
(4, 5), (5, 6), (6, 7), (7, 4), # Top edges
(0, 4), (1, 5), (2, 6), (3, 7) # Vertical edges
]
This list includes the edges that form the perimeter of
the top and bottom faces of the cube, as well as the vertical
edges connecting the top and bottom faces. The first four
edges connect the bottom face, the next four connect the top
face, and the last four connect the top face to the bottom face.
Sujal Choudhari......................................................................................................................................... 43
Surfaces
The surfaces list describes the six faces of the cube.
Each face is defined by a tuple of four vertex indices from the
vertices list, creating a quadrilateral:
[Link] = [
(0, 1, 2, 3), # Front face
(4, 5, 6, 7), # Back face
(0, 1, 5, 4), # Bottom face
(3, 2, 6, 7), # Top face
(0, 3, 7, 4), # Left face
(1, 2, 6, 5) # Right face
]
Each tuple represents the vertices that make up a face
of the cube. For example, (0, 1, 2, 3) describes the front
face of the cube using the vertices at indices 0, 1, 2, and 3.
Similarly, the other tuples define the back face, bottom face,
top face, left face, and right face of the cube.
Sphere
The Sphere class plays a significant role by providing a
detailed model for representing spherical objects in a virtual
environment. This class extends a base class named Geometry,
inheriting fundamental geometric properties and methods
applicable to various shapes. To fully appreciate the Sphere
class, it is essential to explore its attributes and methods, as
well as the underlying algorithms used to define and render a
44 ............................................................................................. Coding Mastery: Rendering Realities
sphere. This discussion will cover these aspects in a
structured and detailed manner.
Figure 5: A sphere without shading alongside GUI
The Sphere class is characterized by several key
attributes that define its geometry and rendering properties.
Understanding these attributes is crucial for grasping how the
class models a sphere in a three-dimensional space.
Sphere class
import math
from [Link] import *
from [Link] import Geometry
Sujal Choudhari......................................................................................................................................... 45
class Sphere(Geometry):
def __init__(self, position, radius, update=None,
slices=15, stacks=15):
super().__init__(position=position,update_callback=up
date)
[Link] = radius
self._slices = slices
self._stacks = stacks
self.generate_vertices()
@property
def slices(self):
return self._slices
@[Link]
def slices(self, value):
self._slices = value
self.generate_vertices()
@property
def stacks(self):
return self._stacks
@[Link]
def stacks(self, value):
self._stacks = value
self.generate_vertices()
def generate_vertices(self):
[Link] = []
[Link] = []
phi_step = [Link] / self._stacks
46 ............................................................................................. Coding Mastery: Rendering Realities
theta_step = 2 * [Link] / self._slices
for i in range(self._stacks + 1):
phi = i * phi_step
sin_phi = [Link](phi)
cos_phi = [Link](phi)
for j in range(self._slices + 1):
theta = j * theta_step
sin_theta = [Link](theta)
cos_theta = [Link](theta)
x = 0 + [Link] * sin_phi *
cos_theta
y = 0 + [Link] * sin_phi *
sin_theta
z = 0 + [Link] * cos_phi
[Link]((x, y, z))
[Link]((sin_phi *
cos_theta, sin_phi * sin_theta, cos_phi))
def render(self, camera_position):
glPushMatrix()
self.apply_transformations()
for i in range(self._stacks):
for j in range(self._slices):
index1 = i * (self._slices + 1) + j
index2 = index1 + self._slices + 1
normal1 = [Link][index1]
normal2 = [Link][index2]
Sujal Choudhari......................................................................................................................................... 47
if not self.is_face_visible(normal1,
camera_position) or not self.is_face_visible(normal2,
camera_position):
continue
glBegin(GL_QUADS)
glNormal3fv([Link][index1])
glVertex3fv([Link][index1])
glNormal3fv([Link][index2])
glVertex3fv([Link][index2])
glNormal3fv([Link][index2 + 1])
glVertex3fv([Link][index2 +
1])
glNormal3fv([Link][index1 + 1])
glVertex3fv([Link][index1 +
1])
glEnd()
glPopMatrix()
Position
The position attribute specifies the coordinates of the
sphere’s center in 3D space. In a three-dimensional coordinate
system, this attribute determines where the sphere is placed
within the scene. Conceptually, imagine a graph with X, Y, and
Z axes. The position is represented by a point where the
sphere’s center resides. For instance, setting the position to (0,
0, 0) places the sphere at the origin of the coordinate system.
48 ............................................................................................. Coding Mastery: Rendering Realities
Radius
The radius attribute defines the size of the sphere. It is
the distance from the center of the sphere to any point on its
surface. Visualize this as the distance from the center of a ball
to its outer edge. The radius is a crucial parameter for
determining the sphere’s size and affects all computations
related to the sphere’s geometry.
Update Callback
The update_callback attribute is an optional function
that is invoked whenever there is a change in the sphere’s
properties. This callback allows for real-time updates and can
be used to trigger additional actions or refresh the sphere’s
state when changes occur. Think of it as a notification system
that alerts you to any modifications made to the sphere’s
attributes.
Slices and Stacks
The slices and stacks attributes control the level of
detail of the sphere’s surface. The slices attribute represents
the number of horizontal bands or divisions around the
sphere, similar to lines of latitude on a globe. The stacks
attribute represents the number of vertical bands or divisions,
akin to lines of longitude. Increasing the number of slices and
stacks results in a smoother, more detailed sphere, similar to
adding more lines to a globe for increased detail.
Sujal Choudhari......................................................................................................................................... 49
The Sphere class includes methods that handle the
sphere’s geometry and rendering. These methods are
fundamental for creating and displaying the sphere in a 3D
environment.
Generate Vertices
The generate_vertices method is responsible for
computing the positions of the vertices on the sphere’s surface
and calculating the corresponding normals. This method uses
spherical coordinates to determine the positions of these
vertices. The process involves dividing the sphere into
horizontal and vertical bands, where each combination of
latitude (ϕ) and longitude (𝜃) generates a vertex. Normals are
vectors perpendicular to the sphere’s surface, calculated at
each vertex to support lighting and shading effects. This
method generates a grid-like structure on the sphere’s surface,
where each point is computed based on the sphere’s radius
and the angles of latitude and longitude.
Render
The render method handles the drawing of the sphere
in the 3D environment. It uses the vertices and normals
generated by the generate_vertices method to create the
visual representation of the sphere. The rendering process
involves creating faces based on the vertices, where each face
is a quadrilateral formed by connecting adjacent vertices. This
method also includes a visibility check to determine which
50 ............................................................................................. Coding Mastery: Rendering Realities
faces are visible from the camera’s perspective. Only the faces
facing the camera are rendered, optimizing performance and
visual fidelity. Here there is a hidden surface removal
algorithm already in place. We will discuss about that in later
chapter.
To better understand how the Sphere class constructs
and renders a sphere, it is helpful to visualize the steps
involved in this process.
Sphere Definition
At the outset, the sphere is defined by its center and
radius. Picture a ball in space, where the center is a specific
point determined by the position attribute, and the radius
extends from this center to the surface of the ball.
Vertex Generation
The sphere is conceptually divided into horizontal and
vertical bands. Imagine slicing an orange into many thin
segments. Each segment represents a horizontal band or slice
on the sphere. Vertices are computed at the intersections of
these bands, forming a grid structure over the sphere’s
surface.
Face Creation
Using the grid of vertices, faces are created to
represent the sphere’s surface. Think of connecting the dots
Sujal Choudhari......................................................................................................................................... 51
on the grid to form a mesh of faces covering the sphere. Each
face is defined by four vertices, and this grid-like mesh covers
the entire surface of the sphere.
Rendering
The final step involves drawing the visible faces of the
sphere. Visualize this as displaying a 3D model on a screen,
where only the faces facing the camera are shown. The
rendering process uses OpenGL commands to draw these
faces, with each face being rendered based on its vertex
positions and normal vectors.
52 ............................................................................................. Coding Mastery: Rendering Realities
Circle
The Circle class serves as a fundamental component for
creating and managing circular shapes. Unlike the Sphere
class, which represents a three-dimensional object, the Circle
class focuses on two-dimensional shapes but is utilized within
a three-dimensional context. By examining the attributes and
methods of the Circle class, one can understand how this class
models a circle and supports its rendering in a 3D
environment. This exploration covers the circle’s design and
the algorithms used to generate and render it. The Circle class
is defined by several core attributes that describe its geometry
and rendering behavior in a 2D plane within 3D space.
import math
from [Link] import *
from [Link] import Geometry
class Circle(Geometry):
def __init__(self, position, radius, update=None,
normal=(0, 0, 1), segments=32):
super().__init__(
position=position,update_callback=update
)
[Link] = radius
[Link] = segments
[Link] = normal # Normal along the z-
axis for the circle
[Link] = []
Sujal Choudhari......................................................................................................................................... 53
for i in range(segments + 1):
theta = 2 * [Link] * i / segments
x = 0 + [Link] * [Link](theta)
y = 0 + [Link] * [Link](theta)
z = 0
[Link]((x, y, z))
@property
def num_segments(self):
return [Link]
@num_segments.setter
def num_segments(self, value):
[Link] = value
[Link] = []
for i in range([Link] + 1):
theta = 2 * [Link] * i / [Link]
x = 0 + [Link] * [Link](theta)
y = 0 + [Link] * [Link](theta)
z = 0
[Link]((x, y, z))
def render(self, camera_direction):
glPushMatrix()
self.apply_transformations()
self.apply_color()
if self.is_face_visible([Link],
camera_direction):
glBegin(GL_POLYGON)
glNormal3fv([Link])
for vertex in [Link]:
glVertex3fv(vertex)
glEnd()
54 ............................................................................................. Coding Mastery: Rendering Realities
glPopMatrix()
Position
The position attribute specifies where the center of the
circle is located in 3D space. Unlike a sphere, which occupies
volume, the circle is a flat, 2D shape, and its position refers to
the center of this shape on the X-Y plane. Imagine a coordinate
system where the position attribute sets the location of the
circle’s center, similar to placing a pin at a point on a flat
surface.
Radius
The radius attribute determines the size of the circle. It
is the distance from the center of the circle to any point on its
circumference. Visualize this as the length from the center of a
drawing compass to the pencil tip. This radius is crucial for
determining the circle's size and affects all calculations related
to the circle’s shape.
Normal
The normal attribute defines the direction of the
circle’s flat plane in 3D space. For a 2D circle, this normal
vector is perpendicular to the plane of the circle. In most
cases, the normal is oriented along the Z-axis, which means
the circle is flat and lies in the X-Y plane. Visualize this as an
Sujal Choudhari......................................................................................................................................... 55
arrow pointing perpendicular to the surface of a coin lying flat
on a table.
Segments
The segments attribute specifies how many individual
line segments make up the circle's perimeter. A higher
number of segments results in a smoother, more circular
appearance. This attribute divides the circumference of the
circle into smaller parts, akin to how a pie chart is divided into
slices. More segments mean the circle will look more like a
true circle with a smoother edge.
The Circle class includes methods that define how the
circle’s geometry is generated and rendered. These methods
are essential for creating the visual representation of the circle
in the 3D environment.
Generate Vertices
The generate_vertices method calculates the vertices
that define the circle’s perimeter. This process involves
dividing the circle into a number of segments, each
representing a part of the circumference. To create these
vertices, the method uses trigonometric functions to
determine the positions of points around the circle. Think of
drawing a series of dots around a circle’s edge using a
compass. Each dot is calculated based on the angle from the
center and the radius of the circle.
56 ............................................................................................. Coding Mastery: Rendering Realities
Render
The render method draws the circle in the 3D space
using OpenGL commands. It involves creating a polygon that
approximates the shape of the circle by connecting the
vertices generated in the generate_vertices method. The
method also includes a visibility check to ensure that the circle
is only drawn if it is facing the camera. Imagine filling in the
outline of the circle you drew with your compass. This method
handles the actual drawing process and adjusts the visual
appearance based on the camera’s perspective. Understanding
how the Circle class creates and renders a circle involves
visualizing several steps in the circle’s construction.
Circle Definition
Initially, the circle is defined by its center position and
radius. Imagine placing a pencil at a point in space and
drawing a circle with a fixed radius. This process sets up the
basic geometric properties of the circle.
Vertex Generation
The circle’s circumference is divided into several
segments. Picture dividing a pizza into slices. Each slice
corresponds to a segment of the circle’s edge. The
generate_vertices method calculates the positions of these
slices, creating a series of points around the circle’s perimeter.
The positions are determined using angles and trigonometric
Sujal Choudhari......................................................................................................................................... 57
functions, with each point being calculated based on the
radius and the angle from the center.
Face Creation
The circle’s perimeter is used to define a polygonal
shape. Visualize connecting the dots around the edge of a
pizza to form the shape of the circle. In the render method,
these vertices are connected to create a flat, polygonal
representation of the circle.
Rendering
The circle is rendered on the screen by filling in the
polygonal shape created from the vertices. Think of filling in
the area of the pizza with color. The render method uses
OpenGL commands to draw the circle, and only the portion of
the circle visible from the camera’s perspective is shown.
OpenGL Commands
During rendering, the glBegin(GL_POLYGON) command
starts the process of drawing the circle’s shape, and
glVertex3fv() specifies the positions of the vertices. The
glNormal3fv() command sets the normal vector for lighting
calculations, and glEnd() finishes the drawing process.
Through these attributes and methods, the Circle class
models a 2D circular shape and supports its rendering within
a 3D graphics environment. By understanding the underlying
58 ............................................................................................. Coding Mastery: Rendering Realities
principles of vertex generation and face creation, one can
appreciate the circle’s role as a basic geometric shape that can
be used to construct more complex objects, such as cylinders.
Cylinder
The Cylinder class in 3D graphics is a sophisticated
representation of a cylindrical object within a three-
dimensional space. This class leverages concepts from simpler
shapes, such as the Circle, to build a more complex structure.
By exploring its attributes and methods, we can gain a
thorough understanding of how a cylinder is constructed and
rendered in a 3D environment.
In previous discussions, we examined the Circle class,
which serves as a foundation for understanding cylindrical
shapes. The Cylinder class extends the principles of the Circle
by incorporating two circular bases and a curved surface that
connects them. This extension involves more complex
geometries but follows a similar logic to that used for circles,
adapted to accommodate the third dimension.
Sujal Choudhari......................................................................................................................................... 59
Figure 6: A cylinder made of circles and a curved surface
At the heart of the Cylinder class are several key
attributes that define its geometric properties. Each of these
attributes plays a role in shaping and positioning the cylinder
within the 3D space.
60 ............................................................................................. Coding Mastery: Rendering Realities
Cylinder Class
import math
import numpy as np
from [Link] import *
from [Link] import Geometry
from [Link] import Circle
class Cylinder(Geometry):
def __init__(self, position, radius, height,
sides=20, update=None):
super().__init__(position=position,
update_callback=update)
[Link] = radius
[Link] = height
[Link] = sides
self.bottom_circle = Circle(
[0, 0, 0],
radius,
normal=(0, 0, -1),
segments=sides,
)
self.top_circle = Circle(
[0, 0, height],
radius,
normal=(0, 0, 1),
segments=sides,
)
[Link] = [(0, 0, -1), (0, 0, 1)] #
Normals for bottom and top faces
Sujal Choudhari......................................................................................................................................... 61
@property
def num_segments(self):
return [Link]
@num_segments.setter
def num_segments(self, value):
[Link] = value
self.bottom_circle.num_segments = value
self.top_circle.num_segments = value
def change_color(self, r, g, b, a=1.0):
self.top_circle.change_color(r, g, b, a)
self.bottom_circle.change_color(r, g, b, a)
return super().change_color(r, g, b, a)
def change_position(self, position):
return super().change_position(position)
def recalculate_normals(self):
rotation_matrix = self.get_rotation_matrix()
# Rotate bottom and top face normals
[Link] = [
tuple([Link](rotation_matrix,
[Link]((0, 0, -1) + (0,)))[:3]),
tuple([Link](rotation_matrix,
[Link]((0, 0, 1) + (0,)))[:3]),
]
def render(self, camera_pos):
glPushMatrix()
self.apply_transformations()
self.apply_color()
# Recalculate normals before rendering
62 ............................................................................................. Coding Mastery: Rendering Realities
self.recalculate_normals()
# Render bottom circle
if self.is_face_visible([Link][0],
camera_pos):
self.bottom_circle.render(camera_pos)
# Render top circle
if self.is_face_visible([Link][1],
camera_pos):
self.top_circle.render(camera_pos)
# Render side faces
glBegin(GL_QUAD_STRIP)
rotation_matrix = self.get_rotation_matrix()
for i in range([Link] + 1):
theta = 2 * [Link] * i / [Link]
x = [Link] * [Link](theta)
y = [Link] * [Link](theta)
z1 = 0
z2 = [Link]
# Calculate normal for the side face
nx = [Link](theta)
ny = [Link](theta)
normal = [Link](rotation_matrix,
[Link]((nx, ny, 0) + (0,)))[:3]
normal = tuple(normal /
[Link](normal)) # Normalize the normal
glNormal3fv(normal)
glVertex3f(x, y, z1)
glVertex3f(x, y, z2)
Sujal Choudhari......................................................................................................................................... 63
glEnd()
glPopMatrix()
The position attribute sets the center of the cylinder’s
base in the 3D coordinate system. Imagine placing a
cylindrical object, like a soda can, on a table; the position
determines where the base of the can sits on that table. This
base is where the height and radius measurements start.
The radius defines the distance from the center of the
base to any point on the perimeter. This attribute directly
influences the width of the cylinder, analogous to the radius of
a circular plate or wheel. It determines how far the sides of the
cylinder extend outward from the central axis.
The height attribute measures the vertical distance
from the base to the top of the cylinder. It describes how tall
the cylinder is, similar to the height of a can or a column. This
height attribute, combined with the radius, determines the
overall dimensions of the cylinder.
The sides attribute indicates the number of segments
used to approximate the curved surface of the cylinder.
Visualize this as the number of vertical strips that make up the
side of a can. More sides mean a smoother, rounder
appearance, akin to increasing the number of sides on a
polygon to make it look more circular.
The Cylinder class employs several methods to manage
its geometry and rendering. These methods ensure that the
64 ............................................................................................. Coding Mastery: Rendering Realities
cylinder's properties are correctly applied and that it is
accurately represented in a 3D scene.
When initializing the Cylinder, two Circle objects are
created to represent the top and bottom faces of the cylinder.
These circles are positioned at the two ends of the cylinder,
and their properties are set based on the radius and sides
attributes. The Circle class’s ability to define a flat, circular
surface is harnessed to provide the cylinder’s bases.
The change_color method updates the color of the
cylinder, affecting both the top and bottom circles as well as
the side surface. This method allows for dynamic changes to
the cylinder’s appearance, much like choosing a new paint
color for an object.
Adjusting the position of the cylinder is managed
through the change_position method. This functionality
moves the cylinder along the X, Y, and Z axes, ensuring that the
entire structure shifts as a whole.
To accurately represent lighting effects, the
recalculate_normals method updates the normal vectors for
the top and bottom faces. Normals are crucial for determining
how light interacts with the surface, and this method
recalculates them to reflect the cylinder’s orientation.
The render method is responsible for drawing the
cylinder in the 3D space. It begins by applying the necessary
Sujal Choudhari......................................................................................................................................... 65
transformations and color changes. The rendering process
involves drawing the top and bottom circles, followed by the
side surface of the cylinder. The side surface is created by
connecting points around the circumference of the top and
bottom circles, forming a series of vertical strips. This method
uses mathematical calculations to determine the vertices of
the side surface and employs OpenGL commands to render
these vertices into a 3D object. By iterating over the segments
around the circle, the method connects the corresponding
points on the top and bottom circles, thus constructing the
cylindrical shape.
To better understand how the Cylinder class works, it
helps to visualize the process of creating and rendering a
cylinder.
Picture starting with two circles, one at the base and
one at the top of the cylinder. These circles are identical in size
and are positioned directly above one another. The Circle class
is used to generate the vertices for these circles, establishing
the top and bottom surfaces of the cylinder.
Next, imagine wrapping a piece of paper around these
two circles to form the side of the cylinder. This paper would
be bent into a curved surface that connects the edges of the
two circles. In the Cylinder class, this curved surface is
approximated using a series of vertical strips or bands. The
render method generates these strips by calculating the
66 ............................................................................................. Coding Mastery: Rendering Realities
positions of points around the circumference of the circles and
connecting them with OpenGL commands.
The curved surface is represented as a series of
connected polygons that form the side of the cylinder. By
calculating the vertices for these polygons and applying
transformations, the render method creates the appearance of
a smooth, continuous surface.
By following these steps, the Cylinder class achieves a
complete 3D representation of a cylindrical shape, from
defining its geometric properties to rendering it in a virtual
space. This process demonstrates how fundamental geometric
shapes can be combined and extended to create more complex
objects in 3D graphics.
Curves
We saw curves in Rendering Realities, but we see them
here in action. Curves play a crucial role in modeling complex
shapes and animations. This section delves into the Curve
class and its specialized extensions, the BezierCurve and
BSplineCurve classes. By examining these classes, we gain
insight into how curves are represented and manipulated in
three-dimensional space.
Sujal Choudhari......................................................................................................................................... 67
Figure 7: Line, Bezier curve and B-Spline curve
The Curve class serves as a foundational structure for
creating and rendering basic curves in 3D space. This class
takes a list of control points as its primary input. These control
points define the path of the curve and determine its shape.
When a Curve object is instantiated, it initializes with a set of
control points and applies a transformation based on the
provided update function.
Rendering a curve involves drawing a series of
connected line segments that pass through or near the control
points. This method creates a visual representation of the
curve, allowing for the creation of simple paths or the basis for
68 ............................................................................................. Coding Mastery: Rendering Realities
more complex geometric structures. The curve is displayed by
connecting consecutive control points with lines, effectively
visualizing the trajectory defined by these points.
Building on the Curve class, the BezierCurve class
introduces a more sophisticated approach to curve design
through the use of Bezier curves. Bezier curves are a type of
parametric curve widely used in graphics and design for their
smooth, flexible properties.
BezierCurve Class
class BezierCurve(Geometry):
def __init__(self, control_points,
num_segments=100, update=None):
super().__init__(position=[0, 0, 0],
update_callback=update)
self.control_points = control_points
self.num_segments = num_segments
def calculate_bezier_point(self, t):
p0, p1, p2, p3 = self.control_points
return (
(1-t)**3 * [Link](p0) +
3 * (1-t)**2 * t * [Link](p1) +
3 * (1-t) * t**2 * [Link](p2) +
t**3 * [Link](p3)
)
def render(self, camera_position):
self.apply_transformations()
self.apply_color()
Sujal Choudhari......................................................................................................................................... 69
glBegin(GL_LINE_STRIP)
for i in range(self.num_segments + 1):
t = i / self.num_segments
point = self.calculate_bezier_point(t)
glVertex3f(point[0], point[1], point[2])
glEnd()
A Bezier curve is defined by a set of control points. In
the BezierCurve class, the control points are used to calculate
the curve's shape through a specific mathematical formula.
The Bezier formula interpolates points between these control
points to create a smooth curve. This formula involves a blend
of the control points, weighted by the parameter 𝑡, which
varies from 0 to 1. By dividing the curve into a series of
segments, the curve can be approximated with a line strip.
The rendering process for Bezier curves involves
calculating points along the curve for a series of 𝑡 values. Each
𝑡 value represents a position on the curve, and by connecting
these points, a visual representation of the curve is created.
The Bezier formula ensures that the curve smoothly
transitions through the control points, resulting in a visually
appealing curve that can be used for a variety of graphical
applications.
The BSplineCurve class extends the concept of curves
by implementing B-spline curves, a more flexible and
powerful type of curve compared to Bezier curves. B-splines
allow for a wider range of curve shapes and provide more
control over the curve's smoothness and flexibility.
70 ............................................................................................. Coding Mastery: Rendering Realities
A B-spline curve is defined by a set of control points, a
degree, and a knot vector. The degree of the curve determines
the level of continuity between curve segments, while the knot
vector specifies how the control points are connected. In the
BSplineCurve class, the B-spline is computed using a
recursive basis function that calculates the influence of each
control point on the curve at a given parameter value t.
BSplineCurve class
class BSplineCurve(Geometry):
def __init__(self, control_points, degree=3,
num_segments=100, update=None):
super().__init__(position=[0, 0, 0],
update_callback=update)
self.control_points = control_points
[Link] = degree
self.num_segments = num_segments
self.knot_vector =
self.create_uniform_knot_vector(len(control_points),
degree)
def create_uniform_knot_vector(self,
num_control_points, degree):
n = num_control_points + degree + 1
return [i for i in range(n)]
def bspline_basis(self, i, k, t, knot_vector):
if k == 0:
return 1.0 if knot_vector[i] <= t <
knot_vector[i + 1] else 0.0
else:
Sujal Choudhari......................................................................................................................................... 71
denom1 = knot_vector[i + k] -
knot_vector[i]
denom2 = knot_vector[i + k + 1] -
knot_vector[i + 1]
term1 = ((t - knot_vector[i]) / denom1 *
self.bspline_basis(i, k - 1, t, knot_vector)) if
denom1 != 0 else 0
term2 = ((knot_vector[i + k + 1] - t) /
denom2 * self.bspline_basis(i + 1, k - 1, t,
knot_vector)) if denom2 != 0 else 0
return term1 + term2
def calculate_bspline_point(self, t):
n = len(self.control_points) - 1
point = [Link](3)
for i in range(n + 1):
basis = self.bspline_basis(i,
[Link], t, self.knot_vector)
point += basis *
[Link](self.control_points[i])
return point
def render(self, camera_position):
self.apply_transformations()
self.apply_color()
glBegin(GL_LINE_STRIP)
for i in range(self.num_segments + 1):
t = (i / self.num_segments) *
(self.knot_vector[-1] - [Link])
point = self.calculate_bspline_point(t)
glVertex3f(point[0], point[1], point[2])
glEnd()
72 ............................................................................................. Coding Mastery: Rendering Realities
The rendering of B-spline curves involves calculating
points along the curve for a range of t values. These points are
then connected to form the curve. The class uses a uniform
knot vector to segment the curve into manageable pieces,
ensuring that the resulting curve is smooth and adheres to the
specified control points and degree.
To understand the construction of these curves, it helps
to visualize the process of creating and rendering them.
For the Curve class, imagine plotting a series of points
on a graph and connecting them with lines. The shape of the
curve is determined by the arrangement of these points.
When visualizing the BezierCurve, think of it as a
smooth path that bends and twists according to the positions
of its control points. The curve starts at the first control point
and ends at the last, with its shape influenced by the
intermediate points.
In the case of the BSplineCurve, envision a more
flexible curve that is influenced by a series of control points
and defined by a set of mathematical rules. The B-spline is like
a chain of smooth segments, each connected to the next, with
the curve's shape controlled by the degree of the spline and
the knot vector.
By understanding these concepts, one can appreciate
how curves are used in 3D graphics to model complex shapes
Sujal Choudhari......................................................................................................................................... 73
and animations. The principles applied in these classes can be
extended to create intricate designs and dynamic scenes in
graphical applications.
Rendering curves involves several stages. Initially, the
control points are defined, setting the stage for the curve's
shape. The apply_transformations and apply_color
methods prepare the curve for rendering, adjusting its
appearance based on transformations and color settings.
For the Curve class, the rendering process involves
drawing a line strip that connects the control points. For the
BezierCurve, the rendering process involves calculating
points along the curve using the Bezier formula and
connecting these points to form the curve. Similarly, for the
BSplineCurve, the rendering process calculates points along
the B-spline curve based on the control points, degree, and
knot vector, and draws a line strip to represent the curve.
The line strips created during the rendering process
visualize the curves as continuous paths. By adjusting
parameters such as the number of segments, control points,
and degree, one can fine-tune the appearance of the curves to
meet specific design requirements.
The Curve, BezierCurve, and BSplineCurve classes
each offer unique capabilities for creating and rendering
curves. Understanding these classes and their methods reveals
the intricate process of designing and visualizing curves in 3D
74 ............................................................................................. Coding Mastery: Rendering Realities
space, highlighting the importance of curves in both simple
and complex graphical applications.
Lights
Now lights are all we require for the basic
implementation of the engine. In the context of 3D graphics,
lighting is fundamental to creating realistic and visually
appealing scenes. The Light class encapsulates the properties
and behavior of a light source within a 3D environment. This
section explores the Light class, delving into its role in 3D
rendering, the methods it provides, and its interaction with
other elements in the scene.
The Light class represents a light source in a 3D space.
Light sources are crucial for simulating how objects are
illuminated in a scene. By controlling various properties of the
light, one can influence the visual appearance of objects,
including their brightness, color, and the shadows they cast.
The Light class is designed to manage these properties
and integrate the light source into the OpenGL rendering
pipeline. It does so by specifying the light’s position and
defining its ambient, diffuse, and specular components. These
components determine how the light interacts with objects in
the scene, contributing to the overall lighting effects.
Sujal Choudhari......................................................................................................................................... 75
Figure 8: Cube with lights enabled
Ambient Light
Ambient light represents a constant level of light that is
present in the scene, regardless of the light’s position. It
ensures that objects are visible even in areas not directly
illuminated by the light source. The ambient component is
specified as a color vector, influencing the overall brightness
of objects in the scene.
76 ............................................................................................. Coding Mastery: Rendering Realities
Diffuse Light
Diffuse light simulates the light scattered in all
directions after hitting an object’s surface. It adds depth and
detail to objects by creating highlights and shading effects. The
diffuse component is also specified as a color vector, affecting
the intensity of light falling on the surfaces.
Specular Light
Specular light simulates the bright spots of light that
appear on shiny surfaces. It creates highlights that enhance
the appearance of reflective materials. The specular
component is represented as a color vector, which influences
the brightness and size of the specular highlights.
These components work together to create realistic
lighting effects. Ambient light ensures visibility, diffuse light
adds detail, and specular light enhances reflections.
The Light class provides several methods to configure
and control the light source:
• __init__ Method: This method initializes the light source
with specific properties. It sets the position of the light in
3D space and defines the ambient, diffuse, and specular
components. The light_id parameter selects which OpenGL
light source to use, such as GL_LIGHT0, GL_LIGHT1, etc.
• change_color Method: This method updates the light’s
ambient, diffuse, and specular colors. It allows for dynamic
Sujal Choudhari......................................................................................................................................... 77
adjustments to the light’s appearance during the rendering
process, enabling real-time changes to the lighting effects.
• render Method: The render method applies the light
properties to the OpenGL context. It enables the light
source, sets its position and color components, and
ensures that lighting effects are applied to the scene. This
method is called during the rendering process to integrate
the light source with the objects being rendered.
• disable Method: This method disables the light source
and the lighting effect. It ensures that the light source is
turned off and that no lighting effects are applied in the
rendering context. This method is useful for switching off
lights or resetting the lighting state.
Visualizing Light in 3D Space
To understand how the Light class affects the 3D scene,
consider the following visual concepts:
• Ambient Light: Imagine a room lit by a single light
bulb. Even if the bulb is turned off, the room still has
some light because of light bouncing from walls and
surfaces. Ambient light in the Light class works
similarly, providing a baseline level of illumination
across the entire scene.
• Diffuse Light: Picture sunlight shining on a ball. The
part of the ball facing the sun is bright, while the other
parts are shaded. Diffuse light in the Light class models
78 ............................................................................................. Coding Mastery: Rendering Realities
this effect, where the intensity of light varies based on
the angle between the light source and the object’s
surface.
• Specular Light: Think of a shiny object reflecting a
flashlight beam. The bright spot on the surface where
the light reflects is an example of specular light. This
effect is simulated by the Light class to create
highlights on reflective surfaces.
The Light Class in Action
from [Link] import *
from [Link] import *
import numpy as np
from [Link] import Geometry
class Light(Geometry):
def __init__(self, position, ambient, diffuse,
specular, light_id=GL_LIGHT0):
super().__init__(position=position)
[Link] = [Link](ambient)
[Link] = [Link](diffuse)
[Link] = [Link](specular)
self.light_id = light_id
def change_color(self, ambient, diffuse,
specular):
[Link] = [Link](ambient)
[Link] = [Link](diffuse)
[Link] = [Link](specular)
def render(self,camera_position):
Sujal Choudhari......................................................................................................................................... 79
glEnable(GL_LIGHTING)
glEnable(self.light_id)
glLightfv(self.light_id, GL_POSITION,
[[Link][0], [Link][1],
[Link][2], 1.0])
glLightfv(self.light_id, GL_AMBIENT,
[Link])
glLightfv(self.light_id, GL_DIFFUSE,
[Link])
glLightfv(self.light_id, GL_SPECULAR,
[Link])
def disable(self):
glDisable(self.light_id)
glDisable(GL_LIGHTING)
The Light class is designed to be used in a scene where
multiple objects are rendered. During the rendering process,
the render method is called to apply the light’s properties to
the scene. This method enables the light source, sets its
parameters, and ensures that the objects in the scene are
illuminated according to the specified light properties.
The change_color method allows for real-time
adjustments to the light’s appearance, while the disable
method provides a way to turn off the light source when it is
no longer needed. By manipulating these methods, one can
achieve various lighting effects, from simple ambient
illumination to complex reflections and highlights.
80 ............................................................................................. Coding Mastery: Rendering Realities
Overall, the Light class plays a vital role in creating
dynamic and visually engaging 3D environments. Its methods
and properties enable precise control over the light’s
appearance and behavior, making it an essential tool for any
3D graphics application.
The Render Pipeline and Light Integration
In the render pipeline, lights are integrated into the
OpenGL context to influence how objects are displayed. The
Light class encapsulates this process by setting up the light’s
parameters and activating the light source. When the render
method is called, the light’s properties are applied to the
scene, affecting how the objects are illuminated. This
integration involves enabling the light source, specifying its
position and color components, and ensuring that the lighting
effects are applied to the objects being rendered.
The render method also demonstrates how lighting is
an integral part of the rendering process, influencing the
visual outcome of the scene. The ability to change light
properties and control the light source through methods like
change_color and disable reflects the flexibility of the Light
class in managing lighting effects.
By understanding the Light class and its methods, one
can effectively use lighting to enhance the appearance of
objects in a 3D scene, creating realistic and visually appealing
graphics.
Sujal Choudhari......................................................................................................................................... 81
Now to use the lights we need to add lights to our renderer,
we store lights in a new object and enable and disable lights as
we render things.
class Renderer:
# …
def render(self):
for light in [Link]:
light.apply_color()
[Link](camera_direction)
for obj in [Link]:
obj.apply_color()
[Link](camera_direction)
for light in [Link]:
[Link]()
And also, we need to modify our add_object method to store
lights separately.
def add_object(self, obj):
if type(obj) is Light:
[Link](obj)
else:
[Link](obj)
Now we have all Geometry components in place, along with a
renderer, now we can manually create the geometry and see
them on our screen. But what fun in that?
Lets add a Free view Camera!
82 ............................................................................................. Coding Mastery: Rendering Realities
Free Camera
In the previous renderer code you might have seen the camera
direction as the input for renderers, that’s for the calculations
of hidden surface removal algorithm, we will take a look into
that after we get a good camera.
from [Link] import *
from src.free_camera import FreeCamera
from [Link] import Light
class Renderer:
def __init__(self):
[Link] = []
[Link] = []
[Link] :FreeCamera= None
def set_camera(self, camera):
[Link] = camera
def add_object(self, obj):
if type(obj) is Light:
[Link](obj)
else:
[Link](obj)
def update(self,delta_time):
for obj in [Link]:
[Link](delta_time)
def render(self):
camera_direction =
[Link].get_direction()
Sujal Choudhari......................................................................................................................................... 83
for light in [Link]:
light.apply_color()
[Link](camera_direction)
for obj in [Link]:
obj.apply_color()
[Link](camera_direction)
for light in [Link]:
[Link]()
The new Renderer class is for managing and rendering 3D
objects and lights in an OpenGL environment. The class
initializes with lists for objects and lights, and a camera that is
initially set to None. The set_camera method assigns a
FreeCamera instance to the renderer. The add_object method
distinguishes between light objects and other objects, adding
them to their respective lists. The update method iterates
through all objects, calling their update methods with the
given delta time. In the render method, the camera's direction
is obtained and used to render the lights and objects. Lights
are first rendered with their color applied, followed by the
rendering of other objects. Finally, lights are disabled to
conclude the rendering process.
And finally, here is the FreeCamera Class:
import pygame
from [Link] import *
from [Link] import *
84 ............................................................................................. Coding Mastery: Rendering Realities
from [Link] import *
import numpy as np
class FreeCamera:
def __init__(self, position, look_at, up):
[Link] = [Link](position,
dtype=np.float32)
self.look_at = [Link](look_at,
dtype=np.float32)
[Link] = [Link](up, dtype=np.float32)
[Link] = 0.0
[Link] = -90.0
[Link] = [Link]([0.0, 0.0, -1.0],
dtype=np.float32)
[Link] = [Link]([1.0, 0.0, 0.0],
dtype=np.float32)
self.world_up = [Link](up,
dtype=np.float32)
self.update_camera_vectors()
def update_camera_vectors(self):
front = [Link]([
[Link]([Link]([Link])) *
[Link]([Link]([Link])),
[Link]([Link]([Link])),
[Link]([Link]([Link])) *
[Link]([Link]([Link]))
], dtype=np.float32)
[Link] = front / [Link](front)
[Link] = [Link]([Link],
self.world_up)
Sujal Choudhari......................................................................................................................................... 85
[Link] /= [Link]([Link])
[Link] = [Link]([Link], [Link])
[Link] /= [Link]([Link])
def get_view_matrix(self):
return gluLookAt(
[Link][0], [Link][1],
[Link][2],
[Link][0] + [Link][0],
[Link][1] + [Link][1], [Link][2] +
[Link][2],
[Link][0], [Link][1], [Link][2]
)
def process_keyboard(self, direction,
delta_time):
velocity = 5.0 * delta_time
if direction == 'FORWARD':
[Link] += [Link] * velocity
if direction == 'BACKWARD':
[Link] -= [Link] * velocity
if direction == 'LEFT':
[Link] -= [Link] * velocity
if direction == 'RIGHT':
[Link] += [Link] * velocity
if direction == 'UP':
[Link] += [Link] * velocity
if direction == 'DOWN':
[Link] -= [Link] * velocity
def process_mouse_movement(self, xoffset,
yoffset, sensitivity=0.1):
xoffset *= sensitivity
yoffset *= sensitivity
86 ............................................................................................. Coding Mastery: Rendering Realities
[Link] += xoffset
[Link] += yoffset
if [Link] > 89.0:
[Link] = 89.0
if [Link] < -89.0:
[Link] = -89.0
self.update_camera_vectors()
def get_direction(self):
return [Link]
The FreeCamera class in the provided code is a pivotal
component for managing a camera in a 3D environment. This
camera can move freely in space and orient itself in various
directions based on user input. The class uses numpy for
mathematical operations and OpenGL for rendering. Let's
explore each part of the class in detail.
Class Attributes and Initialization
The FreeCamera class begins by defining its attributes
in the __init__ method. This method initializes several key
properties of the camera:
• Position: The camera's initial position in 3D space,
represented as a numpy array of three float values.
• Look At: The initial point the camera is directed
towards, also a numpy array of three float values.
Sujal Choudhari......................................................................................................................................... 87
• Up: The up direction vector for the camera, ensuring
the camera's orientation in space, again a numpy array.
• Pitch: An angle representing the camera's up and down
rotation, initialized to zero.
• Yaw: An angle representing the camera's left and right
rotation, initialized to -90 degrees to set the camera
facing forward along the negative Z-axis.
• The class also initializes additional vectors:
• Front: A vector representing the direction the camera
is facing, initialized to point forward.
• Right: A vector perpendicular to the front and up
vectors, pointing to the right.
• World Up: The global up direction in the world space,
used to maintain the camera's orientation.
These vectors are crucial for calculating the camera's
orientation and movement in 3D space.
Camera Vectors Calculation
The update_camera_vectors method recalculates the
camera's direction vectors based on the current yaw and pitch
angles. This involves several mathematical operations:
• Front Vector Calculation: The front vector is derived
from trigonometric functions using the yaw and pitch
angles. The cosine and sine functions compute the
components of this vector, which are then normalized
to maintain a consistent direction and unit length.
88 ............................................................................................. Coding Mastery: Rendering Realities
• Normalization: Normalization ensures that the front
vector has a length of one, crucial for maintaining
accurate movement and orientation calculations.
• Right and Up Vectors: The right vector is calculated as
the cross product of the front vector and the world up
vector. This ensures that the right vector is
perpendicular to both. The up vector is then
recalculated as the cross product of the right and front
vectors, maintaining the orthogonality required for
accurate camera orientation.
Generating the View Matrix
The get_view_matrix method generates a view matrix
using the gluLookAt function. This matrix is essential for
transforming the scene from world coordinates to camera
coordinates, enabling the camera to view the scene correctly.
The gluLookAt function requires:
• Camera Position: The current position of the camera
in 3D space.
• Look At Point: The point the camera is looking at,
computed by adding the front vector to the position
vector.
• Up Vector: The up direction of the camera, ensuring
the camera's orientation is maintained.
Handling Keyboard Input
Sujal Choudhari......................................................................................................................................... 89
The process_keyboard method updates the camera's
position based on keyboard input. This method supports
various movement directions (forward, backward, left, right,
up, down) and calculates the movement velocity based on the
elapsed time (delta time) to ensure smooth and consistent
movement regardless of frame rate.
Velocity Calculation: The velocity is computed as a constant
speed (5.0 units per second) multiplied by the delta time. This
ensures that the camera moves at a consistent speed
irrespective of the frame rate.
Movement Directions: Depending on the specified direction,
the method updates the camera's position vector. For
example, moving forward adds the front vector multiplied by
the velocity to the position, moving backward subtracts it, and
so on for other directions.
Handling Mouse Movement
The process_mouse_movement method adjusts the
camera's orientation based on mouse movement. This method
is crucial for enabling the user to look around in the 3D
environment.
• Sensitivity Adjustment: The x and y offsets
(representing mouse movement) are scaled by a
sensitivity factor to control the responsiveness of the
camera's rotation.
90 ............................................................................................. Coding Mastery: Rendering Realities
• Yaw and Pitch Adjustment: The yaw and pitch angles
are updated based on the scaled mouse offsets. The
yaw angle controls the left and right rotation, while the
pitch angle controls the up and down rotation.
• Pitch Clamping: To prevent the camera from flipping
upside down, the pitch angle is clamped between -89
and 89 degrees. This ensures a realistic and
manageable range of vertical rotation.
Updating Camera Vectors: After adjusting the yaw and pitch
angles, the camera's direction vectors are recalculated using
the update_camera_vectors method to reflect the new
orientation.
Getting the Camera Direction
The get_direction method returns the current front
vector of the camera, indicating the direction in which the
camera is facing. This vector is essential for various
calculations, such as determining the view matrix and
handling camera movement.
Concepts of Roll, Yaw, and Pitch
Understanding the concepts of roll, yaw, and pitch is
essential for grasping how the camera moves and orients itself
in 3D space. These terms are derived from aviation and are
used to describe the rotational movements of an object, such
as an airplane or a camera.
Sujal Choudhari......................................................................................................................................... 91
• Roll: Roll describes the rotation around the
longitudinal axis (front-to-back axis). In an airplane,
rolling involves tilting from side to side, like when a
plane banks left or right. Although roll is not explicitly
managed in this camera implementation, it is an
important concept for understanding 3D orientation.
• Yaw: Yaw describes the rotation around the vertical
axis. In an airplane, yawing involves turning left or
right around a vertical axis, like a compass needle
changing direction. In the FreeCamera class, yaw is
controlled by the horizontal mouse movement,
allowing the camera to look left or right.
• Pitch: Pitch describes the rotation around the lateral
axis (side-to-side axis). In an airplane, pitching involves
moving the nose of the plane up or down, like when the
plane climbs or descends. In the FreeCamera class,
pitch is controlled by the vertical mouse movement,
allowing the camera to look up or down.
Example: Understanding Roll, Yaw, and Pitch
Imagine you're flying an airplane:
Roll: When you tilt the airplane to the left or right, you're
rolling it along its longitudinal axis. This movement is akin to
rotating the airplane around the direction it is flying.
• Yaw: When you turn the airplane left or right to change
direction, you're yawing it around its vertical axis. This
92 ............................................................................................. Coding Mastery: Rendering Realities
movement changes the direction the airplane is
pointing without altering its altitude.
• Pitch: When you raise or lower the airplane's nose,
you're pitching it along its lateral axis. This movement
changes the airplane's altitude, making it climb or
descend.
In the context of the FreeCamera class, roll is not
explicitly handled, as the camera is designed to maintain a
consistent orientation around its front vector. However, yaw
and pitch are directly controlled by the
process_mouse_movement method, allowing the user to look
around in the horizontal and vertical directions respectively.
The FreeCamera class provides a comprehensive way to
simulate a free-moving camera in a 3D environment using
OpenGL and Pygame. By understanding the initialization and
attributes, camera vectors calculation, view matrix generation,
keyboard and mouse input handling, and the concepts of roll,
yaw, and pitch, you can effectively navigate and interact with
3D scenes in your applications.
Hidden Surface Removal
In a 3D environment, objects are composed of
numerous faces (or polygons), each with a normal vector
perpendicular to its surface. When rendering a scene, it's
computationally expensive to draw all faces of all objects,
especially those that are not visible to the camera. Hidden
Sujal Choudhari......................................................................................................................................... 93
surface removal addresses this by determining which faces
are visible and which can be ignored.
Back-face Culling
Back-face culling is one of the simplest forms of hidden
surface removal. It works on the principle that if a face of an
object is oriented away from the camera, it cannot be seen and
thus does not need to be rendered.
Key Concepts:
• Face Normal: A vector perpendicular to the surface of
the face. It indicates which direction the face is
oriented.
• Camera Direction: A vector pointing from the
camera's position towards the direction it is looking.
The is_face_visible Function
The is_face_visible function determines whether a face should
be rendered based on the angle between its normal vector and
the camera direction. Here's a detailed breakdown of how it
works:
Parameters:
• face_normal: The normal vector of the face being
evaluated.
94 ............................................................................................. Coding Mastery: Rendering Realities
• camera_direction: The direction vector from the
camera's viewpoint.
• buffer: A small value used to provide a margin for the
dot product comparison, defaulting to 0.1.
Dot Product Calculation
The function calculates the dot product between the face
normal and the camera direction. The dot product is a
mathematical operation that measures the cosine of the angle
between two vectors, scaled by their magnitudes.
When the dot product is positive, the angle between
the vectors is less than 90 degrees, meaning the face is
oriented towards the camera. When the dot product is
negative, the angle is greater than 90 degrees, indicating the
face is oriented away from the camera. When the dot product
is zero, the vectors are perpendicular, meaning the face is
viewed edge-on.
Visibility Determination:
The function then compares the dot product to the
buffer value. If the dot product is less than or equal to the
buffer, the face is considered visible. This slight buffer allows
for some flexibility in the visibility determination.
Sujal Choudhari......................................................................................................................................... 95
Code Breakdown:
This function will be added to geometry class, so out other
classes can use it.
def is_face_visible(self, face_normal,
camera_direction, buffer=0.1):
dot_product = [Link](face_normal,
camera_direction)
return dot_product <= buffer or True
Example and Visualization
Imagine a cube in a 3D scene with a camera looking at it. The
cube has six faces, each with its normal vector:
• Front Face Normal: Points outwards from the front.
• Back Face Normal: Points outwards from the back.
• Left Face Normal: Points outwards from the left side.
• Right Face Normal: Points outwards from the right
side.
• Top Face Normal: Points upwards.
• Bottom Face Normal: Points downwards.
The camera direction is a vector pointing from the
camera's position to the cube. As the camera moves around
the cube, the is_face_visible function determines which
faces are visible based on their normals and the camera
direction. If the camera is in front of the cube, the front face's
normal will have a positive dot product with the camera
direction, making it visible. The back face's normal will have a
96 ............................................................................................. Coding Mastery: Rendering Realities
negative dot product, indicating it is facing away from the
camera and thus not visible.
CHAPTER THREE
Enhancements and Advanced
Techniques
“
Computer graphics is a way for humans to visualize,
understand and interact with information.
~ Andries van Dam
Custom Save File
Every time we have to create a Geometry, we manually
have to create the python object, and then pass it to renderer.
It would be nice to have an intermediatory to do that for us.
Picture this, we write out geometry data as a JSON file
and it can be automatically parsed for the objects and details.
{"objects": {
"line": {
Sujal Choudhari......................................................................................................................................... 97
"type": "line",
"origin": [-5,2,0],
"destination": [-3,1, 0]
},}
For example, take a look at this JSON, it has details
about the name of the object, type, origin and destination, as
our line class needs both origin and destination. On the
contrary, a Cube JSON should look something like this
"cube": {
"type": "cube",
"position": [0,0,0],
"size": [1,1,1]}
As we need position and size for out Cube constructor,
its JSON will have that information. Now we need to make a
parser to convert this information into actual Geometry
objects and render them.
Scene Class
import json
from [Link] import Line
from [Link] import Cube
from [Link] import Sphere
from [Link] import Cylinder
from [Link] import Light
from [Link] import Curve, BezierCurve,
BSplineCurve
class Scene:
def __init__(self, app, scene_path):
98 ............................................................................................. Coding Mastery: Rendering Realities
with open(scene_path, "r") as f:
str_data = [Link]()
json_data = [Link](str_data)
self.init_objects = json_data["objects"]
[Link] = app
[Link] = []
[Link] = []
[Link] = -1
[Link]()
def init(self):
[Link] = []
[Link] = []
for key, obj in self.init_objects.items():
print(key, obj)
if obj["type"] == "line":
line = Line(obj["origin"],
obj["destination"])
[Link](line)
elif obj["type"] == "cube":
cube = Cube(obj["position"],
[Link]("size", (1, 1, 1)))
cube.change_color(*[Link]("color",
(1, 1, 1, 1)))
[Link](cube)
elif obj["type"] == "sphere":
sphere = Sphere(obj["position"],
obj["radius"], slices=15, stacks=15)
sphere.change_color(*[Link]("color",
(1, 1, 1, 1)))
[Link](sphere)
elif obj["type"] == "cylinder":
cylinder = Cylinder(
Sujal Choudhari......................................................................................................................................... 99
obj["position"], obj["radius"],
obj["height"], sides=15
)
cylinder.change_color(*[Link]("color", (1, 1, 1,
1)))
[Link](cylinder)
elif obj["type"] == "curve":
curve = Curve(
control_points=obj["control_points"],
)
[Link](curve)
elif obj["type"] == "bezier_curve":
curve = BezierCurve(
control_points=obj["control_points"],
num_segments=[Link]("num_segments", 100),
)
[Link](curve)
elif obj["type"] == "bspline_curve":
curve = BSplineCurve(
control_points=obj["control_points"],
degree=[Link]("degree", 3),
num_segments=[Link]("num_segments", 100),
)
[Link](curve)
elif obj["type"] == "light":
light = Light(
position=obj["position"],
ambient=obj["ambient"],
100 ......................................................................................... Coding Mastery: Rendering Realities
diffuse=obj["diffuse"],
specular=obj["specular"],
)
[Link](light)
[Link](light) # Add
light to objects list for UI rendering
if "keyframes" in obj:
[Link][-1].keyframes =
obj["keyframes"]
[Link].add_object([Link][-1])
Introduction to the Scene Class
The Scene class is responsible for initializing and
managing a 3D scene based on data loaded from a JSON file. It
utilizes various geometric objects and lights to construct the
scene, preparing it for rendering in a 3D environment.
Class Attributes and Initialization
The Scene class initializes several attributes and loads
scene data from a JSON file during initialization. Here's a
detailed breakdown:
1. Initialization from JSON Data: The constructor
(__init__ method) takes two parameters:
o app: An application object or context used to
interact with the rendering engine.
Sujal Choudhari..................................................................................................................................... 101
o scene_path: Path to the JSON file containing
scene data.
It reads the JSON data from scene_path, parses it, and
stores the initial objects configuration (init_objects) and the
application context (app). We have not discussed the app yet,
but it will be a necessary element to swap out scenes with new
one and make out Rendering Engine more feature rich, so that
you can expand upon it to make a complete Game Engine.
2. Attributes:
o init_objects: Dictionary containing initial
object configurations loaded from the JSON file.
o app: Reference to the application context.
o objects: List to hold instantiated 3D objects
that compose the scene.
o lights: List to store light sources within the
scene.
o sceneID: Identifier for the scene, initialized to -
1.
Initialization (init Method)
The init method initializes the scene by instantiating
objects based on the data parsed from the JSON file. Here’s a
breakdown of its functionality:
102 ......................................................................................... Coding Mastery: Rendering Realities
1. Resetting Lists: Clears any previous objects and lights to
ensure a clean initialization.
2. Iterating Over init_objects:
o For each key-value pair in init_objects:
▪ Determine the type of object (line, cube,
sphere, cylinder, curve, bezier_curve,
bspline_curve, light) based on the "type"
field in the JSON data.
3. Object Instantiation:
o Depending on the type of object:
▪ Instantiate the corresponding class (Line,
Cube, Sphere, Cylinder, Curve,
BezierCurve, BSplineCurve, Light) with
parameters extracted from the JSON data.
▪ Customize properties such as position,
size, radius, color, control points,
etc., as specified in the JSON data.
4. Keyframes Assignment:
o If "keyframes" are specified in the JSON data for an
object, assign them to the object's keyframes
attribute. This step prepares for future animation
handling.
Sujal Choudhari..................................................................................................................................... 103
5. Adding Objects to Lists:
o Append the instantiated objects to the objects list.
o If the object is a Light, also add it to the lights list
for specific lighting management.
6. Integration with Renderer:
o Register each instantiated object with the renderer
([Link]) by calling
[Link].add_object(). This ensures that
objects are prepared for rendering in the 3D scene.
Concepts of Animation (Keyframes)
Wait, did I just say Keyframes? Are we adding
Animations to the Engine? Yes! We will be adding keyframes
based animation so our cubes can dance and change colours.
Animation in 3D graphics involves changing the
properties of objects over time, often referred to as keyframe
animation. Keyframes are specific frames within a sequence
that define the starting or ending points of an animation
transition. In this implementation:
• Each object that supports animation (Line, Cube, Sphere,
Cylinder, Curve, BezierCurve, BSplineCurve, Light) can
have keyframes defined in the JSON data.
104 ......................................................................................... Coding Mastery: Rendering Realities
• Keyframes typically specify changes in position,
orientation, size, colour, etc., over a series of frames or
time intervals.
• Handling of keyframes would involve interpolating
between keyframes to smoothly transition object
properties, which is typically handled in the animation
subsystem beyond the scope of this initialization process.
Keyframe-Based Animation
Keyframe-based animation is a technique used in
computer graphics to interpolate between predefined states of
an object over time, creating smooth motion and changes in
attributes such as position, rotation, and colour. The update
method in the Geometry class exemplifies this technique by
smoothly transitioning between keyframes based on an
interpolation parameter (_t).
Sujal Choudhari..................................................................................................................................... 105
Figure 9: A scene with multiple objects in animation
Geometry Class
class Geometry:
def __init__(
self,
position=(0.0, 0.0, 0.0),
rotation=(0.0, 0.0, 0.0),
color=(1.0, 1.0, 1.0, 1.0),
keyframes=[],
update_callback=None,
):
self.update_callback = update_callback
[Link] = [Link](position,
dtype=np.float32)
106 ......................................................................................... Coding Mastery: Rendering Realities
[Link] = [Link](rotation,
dtype=np.float32)
[Link] = [Link](color,
dtype=np.float32)
[Link] = keyframes
self._current_keyframe = 0
self._t = 0.0 # Interpolation parameter
self._delta_time = 1.0 # Time interval to
switch between keyframes
def update(self, delta_time):
if len([Link]) < 2:
return
self._t += delta_time / self._delta_time
if self._t >= 1.0:
self._t = 0.0
self._current_keyframe =
(self._current_keyframe + 1) % len([Link])
next_keyframe = (self._current_keyframe + 1)
% len([Link])
current_kf =
[Link][self._current_keyframe]
next_kf = [Link][next_keyframe]
if 'position' in current_kf and 'position' in
next_kf:
[Link] =
self.lerp_vector(current_kf['position'],
next_kf['position'], self._t)
if 'rotation' in current_kf and 'rotation' in
next_kf:
Sujal Choudhari..................................................................................................................................... 107
[Link] =
self.lerp_vector(current_kf['rotation'],
next_kf['rotation'], self._t)
if 'color' in current_kf and 'color' in
next_kf:
[Link] =
self.lerp_color(current_kf['color'],
next_kf['color'], self._t)
if self.update_callback:
self.update_callback()
Overview of the update Method
The update method in the Geometry class facilitates
keyframe-based animation by interpolating object properties
between consecutive keyframes. Here’s a detailed breakdown
of its functionality:
Initialization
1. Parameters: delta_time represents the time passed
since the last update, allowing for frame-rate-
independent animation.
2. Precondition: Checks if there are at least two
keyframes (len([Link]) < 2). If fewer,
animation cannot proceed, and the method returns
early.
108 ......................................................................................... Coding Mastery: Rendering Realities
Interpolation Parameter (_t)
1. Increment: Updates _t based on delta_time relative
to _delta_time. This incrementation controls how
quickly the object transitions between keyframes.
2. Looping: If _t reaches or exceeds 1.0, it resets to 0.0
and increments _current_keyframe to move to the
next set of keyframes. This looping mechanism allows
seamless looping of animations.
Interpolation between Keyframe
Current and Next Keyframes: Identifies the current and
next keyframes using _current_keyframe and next_keyframe.
1. Attributes: Interpolates (lerp) between attributes such
as position, rotation, and color if these attributes
exist in both current and next keyframes.
2. Interpolation Function (lerp): Linear interpolation
(lerp) calculates intermediate values between start
(current_kf) and end (next_kf) values based on _t.
This function ensures smooth transitions between
keyframes.
Update Callback
If update_callback is provided and callable, it is
invoked after updating object attributes. This mechanism
allows external systems to react to changes in object state,
facilitating advanced interactions or UI updates.
Sujal Choudhari..................................................................................................................................... 109
Linear Interpolation (lerp) Functions
The Geometry class provides three lerp methods
tailored for different types of attributes:
• lerp_vector: Interpolates between two 3D vector
positions (position and rotation).
• lerp_color: Interpolates between two RGBA colour
values (color).
• lerp: Generalized linear interpolation function used by
lerp_vector and lerp_color.
That’s it only one change and we have working keyframe
animations, but these are only for position scale and rotation,
you can add more such keyframes for each and individual
objects. For example, you can animate the number of steps a
sphere has. We have endless potential for animations. You can
explore them from here.
Debug Editor Window
Before we add an Application class, which is the final puzzle
piece of the entire renderer, It would be better to add a Debug
window, that can help us customize objects while the program
is running. Being too lazy to even edit the now even simpler
JSON objects can help you learn how professionals make a
game engine. While shipping the game, the Engines just strip
out the Editor Window and its API to the core engine.
110 ......................................................................................... Coding Mastery: Rendering Realities
I will be using IMGUI, it is a traditionally C++ tool to render
debug windows on game engines like projects, but it as a
python library as well, so why not give it a try?
ImGuiHandler Class
import numpy as np
import imgui
from [Link] import PygameRenderer
from [Link] import Line
from [Link] import Cube
from [Link] import Sphere
from [Link] import Cylinder
from [Link] import Light
from [Link] import Curve, BezierCurve,
BSplineCurve
class ImGuiHandler:
def __init__(self, window, objects):
imgui.create_context()
self.imgui_renderer = PygameRenderer()
[Link] = window
[Link] = objects
def render_transforms(self):
[Link]("Objects")
for obj in [Link]:
obj_name = obj.__class__.__name__
if imgui.tree_node(obj_name):
position = list([Link])
changed, new_position =
imgui.input_float3("Position", *position)
Sujal Choudhari..................................................................................................................................... 111
if changed:
obj.change_position(
new_position[0], new_position[1],
new_position[2]
)
if hasattr(obj, "control_points"):
list_vertices = obj.control_points
for i, vertex in enumerate(list_vertices):
changed, new_vertex = imgui.input_float3(
f"Points {i+1}", *vertex
)
if changed:
list_vertices[i] = new_vertex
obj.control_points = list_vertices
if hasattr(obj, "num_segments"):
num_segments = obj.num_segments
changed, new_num_segments =
imgui.input_int(
"Number of Segments", num_segments
)
if changed:
obj.num_segments = new_num_segments
if type(obj) is Sphere:
slices, stacks = [Link], [Link]
changed1, new_slices =
imgui.input_int("Slices", slices)
changed2, new_stacks =
imgui.input_int("Stacks", stacks)
if changed1:
[Link] = new_slices
if changed2:
112 ......................................................................................... Coding Mastery: Rendering Realities
[Link] = new_stacks
if hasattr(obj, "color") and type(obj) is not
Light:
color = list([Link])
changed, new_color =
imgui.color_edit4("Color", *color)
if changed:
obj.change_color(*new_color)
if hasattr(obj, "ambient") and type(obj) is
Light:
ambient = list([Link])
diffuse = list([Link])
specular = list([Link])
changed1, new_ambient =
imgui.color_edit3("Ambient", *ambient)
changed2, new_diffuse =
imgui.input_float3("Diffuse", *diffuse)
changed3, new_specular =
imgui.input_float3("Specular", *specular)
if changed1 or changed2 or changed3:
obj.change_color(new_ambient,
new_diffuse, new_specular)
if hasattr(obj, "rotation"):
rotation = list([Link])
changed, new_rotation =
imgui.input_float3("Rotation", *rotation)
if changed:
obj.change_rotation(
new_rotation[0], new_rotation[1],
new_rotation[2]
)
# Add more UI controls for other properties
as needed
Sujal Choudhari..................................................................................................................................... 113
imgui.tree_pop()
[Link]()
def handle_event(self, event):
self.imgui_renderer.process_event(event)
def render_ui(self):
imgui.get_io().display_size = ([Link],
[Link])
imgui.new_frame()
self.render_transforms()
[Link]()
self.imgui_renderer.render(imgui.get_draw_data())
The ImGuiHandler class in the provided code is
responsible for rendering and managing an editor window
using the ImGui library, specifically tailored to work with
Pygame and the ImGui-Pygame integration. The editor
window is designed to allow users to interactively modify
properties of various 3D objects in a scene, providing a user-
friendly interface for transformations and adjustments.
Initialization and Context Setup
The __init__ method of the ImGuiHandler class is
where the initial setup occurs. This method takes two
parameters: window and objects. The window parameter
represents the Pygame window where the ImGui context will
be rendered, and objects is a list of 3D objects that the user
can interact with. Within this method, an ImGui context is
created using imgui.create_context(), and a
114 ......................................................................................... Coding Mastery: Rendering Realities
PygameRenderer instance is initialized to handle the
rendering of ImGui within the Pygame environment. This
setup ensures that ImGui can interface with Pygame and
render the GUI elements correctly.
Rendering Object Transforms
The core functionality of the editor window is
encapsulated in the render_transforms method. This method
is responsible for rendering the ImGui window and providing
interactive controls for the properties of each object in the
scene.
• ImGui Window Setup: The method begins by creating a
new ImGui window named "Objects" using
[Link]("Objects"). This window will house the
interactive controls for the various properties of the
objects.
• Iterating Through Objects: The method iterates over the
list of objects provided during initialization. For each
object, it retrieves the class name using
obj.__class__.__name__, which serves as the identifier
for the tree node in ImGui.
• Tree Node for Each Object: A tree node is created for
each object using imgui.tree_node(obj_name). Tree
nodes allow for expandable and collapsible sections in the
ImGui window, providing a neat and organized interface.
Sujal Choudhari..................................................................................................................................... 115
• Position Control: For each object, the position is retrieved
and displayed using imgui.input_float3. This creates
three input fields for the X, Y, and Z coordinates of the
object's position. If the user changes any of these values,
the object's position is updated accordingly using the
change_position method.
• Curve Control Points: If the object has control points
(typically for curves), these are displayed and can be
edited interactively. The method iterates through the
control points, creating input fields for each one. If any
control point is changed, the object's control points are
updated.
• Segment Control for Curves: For objects with a
num_segments attribute (such as certain types of curves),
an input field is provided to adjust the number of
segments. Changes are applied to the object's attribute
directly.
• Sphere Slices and Stacks: For sphere objects, additional
input fields are provided to adjust the slices and stacks
attributes, which control the level of detail of the sphere's
geometry. Changes are applied to the object's attributes.
• Color Control: For objects with a color attribute, a color
picker is provided using imgui.color_edit4. This allows
the user to interactively change the color of the object. If
the object is a light source, separate controls for ambient,
diffuse, and specular colors are provided.
116 ......................................................................................... Coding Mastery: Rendering Realities
• Rotation Control: For objects with a rotation attribute,
input fields are provided for the X, Y, and Z rotation angles.
Changes are applied using the change_rotation method.
• Additional Controls: The method is designed to be
extensible, allowing additional controls to be added as
needed for other object properties.
Event Handling
The handle_event method processes Pygame events
and passes them to the ImGui renderer. This integration
ensures that user interactions with the Pygame window, such
as mouse clicks and keyboard inputs, are correctly processed
by ImGui, allowing for seamless interaction with the GUI
elements.
UI Rendering
The render_ui method is responsible for rendering the
ImGui frame. It sets the display size based on the Pygame
window dimensions, begins a new ImGui frame, calls the
render_transforms method to render the object controls, and
finally renders the ImGui draw data using the
PygameRenderer. This method ensures that the ImGui
elements are correctly drawn on the screen, providing a real-
time interface for the user to interact with the 3D objects. The
ImGuiHandler class provides a comprehensive and interactive
editor window for manipulating 3D object properties in a
scene. By leveraging ImGui and Pygame, it offers a user-
Sujal Choudhari..................................................................................................................................... 117
friendly interface that allows for real-time adjustments of
object attributes such as position, rotation, colour, and more.
This class serves as a crucial component in developing
interactive and dynamic 3D applications, facilitating ease of
use and enhancing the overall user experience.
Application
Finally, after all that we can add our most awaited
Application class.
Application Class
import pygame
from [Link] import *
from [Link] import *
from [Link] import Window
from [Link] import Renderer
from src.free_camera import FreeCamera
from [Link] import ImGuiHandler
from [Link] import Window
from [Link] import Renderer
class Application:
def __init__(
self,
width=800,
height=600,
title="Rendering Engine",
118 ......................................................................................... Coding Mastery: Rendering Realities
update_callback=None,
):
[Link]()
[Link] = Window(width=width,
height=height, title=title)
[Link] = Renderer()
[Link] = [Link]()
[Link] = FreeCamera([0, 0, 3], [0, 0, -
1], [0, 1, 0])
[Link].set_visible(False)
[Link].set_grab(True)
self.mouse_locked = True
[Link] = []
self.update_callback = update_callback
def add_scene(self, scene):
[Link](scene)
[Link] = len([Link]) - 1
def load_scene(self, sceneID):
[Link]()
[Link]()
[Link] = Renderer()
scene = [Link][sceneID]
[Link]()
[Link].set_camera([Link])
self.imgui_handler =
ImGuiHandler([Link], [Link])
def handle_events(self):
for event in [Link]():
if [Link] == [Link]:
[Link]()
if [Link] == [Link] and
Sujal Choudhari..................................................................................................................................... 119
[Link] == pygame.K_ESCAPE:
self.toggle_mouse_lock()
self.imgui_handler.handle_event(event)
def toggle_mouse_lock(self):
self.mouse_locked = not self.mouse_locked
[Link].set_visible(not
self.mouse_locked)
[Link].set_grab(self.mouse_locked)
def process_input(self):
if self.mouse_locked:
keys = [Link].get_pressed()
delta_time = [Link].get_time() /
1000.0
if keys[pygame.K_w]:
[Link].process_keyboard("FORWARD", delta_time)
if keys[pygame.K_s]:
[Link].process_keyboard("BACKWARD", delta_time)
if keys[pygame.K_a]:
[Link].process_keyboard("LEFT",
delta_time)
if keys[pygame.K_d]:
[Link].process_keyboard("RIGHT",
delta_time)
if keys[pygame.K_q]:
[Link].process_keyboard("UP",
delta_time)
if keys[pygame.K_e]:
[Link].process_keyboard("DOWN",
delta_time)
120 ......................................................................................... Coding Mastery: Rendering Realities
x, y = [Link].get_rel()
[Link].process_mouse_movement(x, -y)
def run(self):
while [Link].wait_for_close():
[Link]()
self.handle_events()
self.process_input()
if self.update_callback:
self.update_callback()
glLoadIdentity()
[Link].get_view_matrix()
[Link]([Link].get_time() / 1000.0)
[Link]()
self.imgui_handler.render_ui()
[Link]()
[Link](60)
[Link]()
The Application class serves as the main control center
for the 3D rendering engine, coordinating various components
like the window, renderer, camera, scenes, and user input.
Here's a detailed breakdown of its functionality:
Initialization
The __init__ method sets up the essential components of the
application:
1. Pygame Initialization: [Link]() initializes all the
imported Pygame modules.
Sujal Choudhari..................................................................................................................................... 121
2. Window Creation: A Window object is created with the
specified width, height, and title, setting up the main
display window for the application.
3. Renderer Initialization: The Renderer object handles the
drawing of 3D objects.
4. Clock Setup: A [Link] object is used to control
the frame rate of the application.
5. Camera Setup: A FreeCamera object is initialized to
manage the viewpoint and movement in the 3D space.
6. Mouse Configuration: The mouse is initially hidden and
locked to the window to facilitate camera movement.
7. Scene Management: The scenes list will store different
scenes that can be loaded and rendered.
8. Callback Setup: An optional update_callback can be
provided to execute additional logic during the update
cycle.
Scene Management
The add_scene and load_scene methods manage the scenes:
1. add_scene: Appends a new scene to the scenes list and
assigns it a unique sceneID.
2. load_scene: Clears the current window, resets the
renderer, initializes the specified scene, sets the
122 ......................................................................................... Coding Mastery: Rendering Realities
camera, and initializes the ImGui handler with the
objects of the scene.
Event Handling
The handle_events method processes user input and system
events:
1. Event Loop: Iterates through Pygame events, handling
window close events and toggling mouse lock state on
pressing the Escape key.
2. ImGui Event Handling: Passes events to the ImGui
handler to manage GUI interactions.
Mouse Lock Toggle
The toggle_mouse_lock method toggles the visibility
and locking state of the mouse, enhancing the user experience
by allowing them to switch between interacting with the GUI
and controlling the camera.
Input Processing
The process_input method handles keyboard and mouse
input for camera movement:
1. Keyboard Input: Processes movement keys (W, A, S, D, Q,
E) to move the camera in different directions.
Sujal Choudhari..................................................................................................................................... 123
2. Mouse Movement: Processes relative mouse movement
to adjust the camera's view direction.
Main Loop
The run method contains the main loop of the application:
1. Window Check: Continuously checks if the window
should remain open.
2. Clearing and Handling Events: Clears the window,
handles events, and processes user input.
3. Update Callback: Executes the optional update_callback
if provided.
4. Camera and Renderer Updates: Updates the camera
view matrix, renderer state, and renders the scene.
5. ImGui Rendering: Renders the ImGui UI elements.
6. Window Update: Swaps the window buffers and controls
the frame rate to 60 FPS.
7. Quit Pygame: Terminates Pygame upon exiting the loop.
124 ......................................................................................... Coding Mastery: Rendering Realities
Suggestions for Improvement
1. Error Handling: Add error handling for critical operations
such as initializing components and loading scenes to
ensure the application can gracefully handle failures.
2. Configurable Controls: Implement a configuration file or
settings menu to allow users to customize key bindings
and other parameters.
3. Scene Persistence: Add functionality to save and load
scenes from files to enhance usability and flexibility.
4. Modularization: Further modularize the code by breaking
down large methods into smaller, more manageable
functions.
5. Performance Optimization: Profile and optimize the
rendering pipeline to improve performance, especially for
complex scenes.
6. Documentation: Add comprehensive documentation and
comments to improve code readability and
maintainability.
7. User Feedback: Implement visual feedback for user
actions, such as highlighting selected objects or providing
status messages.
The Application class is a robust foundation for a 3D
rendering engine, integrating various components and
Sujal Choudhari..................................................................................................................................... 125
providing a structured approach to manage scenes, handle
user input, and render 3D objects. By following the
suggestions for improvement, the code can be made more
flexible, user-friendly, and maintainable, enhancing the overall
functionality and user experience of the application.
Using it all together
def main():
app = Application(width=1000, height=800,
title="Rendering Engine")
scene = Scene(app, "./assets/[Link]")
app.add_scene(scene)
app.load_scene([Link])
[Link]()
if __name__ == "__main__":
main()
We can now successfully load scenes and run our app.
126 ......................................................................................... Coding Mastery: Rendering Realities
CHAPTER FOUR
Where can we take this from here?
“
We don't just read pixels, we read emotion.
~ Edwin Catmull
The journey of making a working Rendering engine has ended
here but, its not the end of Engines. You can take the engine to
a next level by implementing various other technologies, that
competitors have. Below I have given few of the ideas you can
implement and make your engine a product that you can
launch.
Advanced Lighting Models
Implementing advanced lighting models like global
illumination, ambient occlusion, and real-time ray tracing can
significantly enhance the realism of rendered scenes. Global
illumination simulates the way light bounces around a scene,
capturing indirect lighting and reflections for a more natural
look. Ambient occlusion adds depth by darkening creases,
holes, and surfaces that are close to each other, creating a
more grounded appearance. Real-time ray tracing, on the
other hand, simulates the path of light rays as they interact
with objects, producing highly realistic reflections, refractions,
Sujal Choudhari..................................................................................................................................... 127
and shadows. These techniques collectively bring a level of
photorealism to the engine that is highly desirable for modern
graphics applications.
Shadow Mapping
Shadow mapping allows objects in the scene to cast shadows,
which are essential for adding depth and realism. By using a
shadow map, which is a texture that stores the depth
information from the perspective of the light source, the
engine can determine whether a point in the scene is in
shadow or illuminated. This technique can be further
enhanced with features like soft shadows, which create more
natural and realistic shadow edges, and dynamic shadows,
which move and change with the light source. Implementing
shadow mapping will make scenes more immersive and
visually appealing.
Physics Integration
Integrating a physics engine, such as Bullet or PhysX, will
enable the simulation of realistic physical interactions,
collisions, and dynamics within the scene. This addition allows
for the creation of interactive environments where objects can
respond to forces, gravity, and collisions in a natural manner.
For instance, a character might push a box, causing it to slide
and tumble realistically, or a glass object could shatter upon
impact. Physics integration is crucial for applications like
128 ......................................................................................... Coding Mastery: Rendering Realities
games and simulations where real-world behaviour is
essential for immersion.
Particle Systems
Particle systems are used to simulate fuzzy phenomena such
as fire, smoke, rain, and explosions. By defining a large
number of small particles that collectively behave according to
certain rules, the engine can create complex visual effects.
Particle systems can add a dynamic element to the scene,
making it more lively and visually interesting. For example, a
fireplace can have realistic flickering flames and rising smoke,
or a battlefield can feature explosive effects and flying debris.
Particle systems are a powerful tool for enhancing the visual
storytelling of the engine.
Animation System
Developing a robust animation system is essential for bringing
characters and objects to life. This system should support
skeletal animation, where a skeleton of bones is animated and
drives the deformation of a mesh, as well as morph targets,
where specific vertex positions are interpolated to achieve
facial expressions or other deformations. Animation blending
allows for smooth transitions between different animations,
making movements appear more fluid and natural. A
sophisticated animation system can transform static scenes
into dynamic and engaging experiences.
Sujal Choudhari..................................................................................................................................... 129
Procedural Generation
Procedural generation techniques allow for the algorithmic
creation of complex terrains, textures, and structures. This
approach can generate vast and varied landscapes, detailed
textures, and intricate architectures without manual
intervention. For instance, a procedural terrain generator can
create mountains, valleys, and rivers based on parameters like
height maps and noise functions. Procedural generation is
particularly useful for creating expansive game worlds and
reducing the workload of artists and designers.
Virtual Reality (VR) Support
Adding support for VR headsets can provide immersive
experiences, making the engine suitable for VR applications
and games. VR support involves rendering scenes from the
perspective of the VR headset and handling the user’s
interactions and movements in a three-dimensional space.
This capability allows users to explore and interact with
virtual environments in a highly immersive and intuitive
manner. Implementing VR support opens up new possibilities
for gaming, training simulations, architectural visualization,
and more.
Artificial Intelligence (AI)
Incorporating AI techniques can enhance the behaviour and
interactions of non-player characters (NPCs) within the scene.
130 ......................................................................................... Coding Mastery: Rendering Realities
AI can drive NPC decision-making, pathfinding, and adaptive
behaviours, making them appear more intelligent and
responsive. For example, NPCs can navigate complex
environments, avoid obstacles, and react to player actions in a
believable way. AI can also be used to generate procedural
content, such as dynamically adjusting the difficulty of a game
or creating new levels based on player behaviour. Integrating
AI into the engine can create more engaging and dynamic
experiences.
Networked Multiplayer
Developing networking capabilities to support multiplayer
features allows multiple users to interact and collaborate in
shared 3D environments. This addition involves synchronizing
the state of the game world across different clients and
handling communication between players. Implementing
networked multiplayer can transform single-player
experiences into social and collaborative activities, making the
engine suitable for online games, virtual meetings, and
cooperative simulations. It opens up possibilities for creating
persistent worlds where users can build, interact, and explore
together.
Asset Pipeline
Enhancing the asset pipeline with support for various 3D
model formats, texture compression, and efficient resource
Sujal Choudhari..................................................................................................................................... 131
management is crucial for a smooth workflow. An advanced
asset pipeline allows for easy import and export of assets from
popular 3D modelling tools, ensuring compatibility and ease
of use. Texture compression techniques reduce the memory
footprint and loading times of textures, improving
performance. Efficient resource management ensures that
assets are loaded and unloaded as needed, optimizing memory
usage and performance. A robust asset pipeline streamlines
the development process and enhances the engine’s usability.
Post-Processing Effects
Post-processing effects like bloom, depth of field, motion blur,
and colour grading can significantly enhance the visual quality
of rendered scenes. Bloom simulates the blurring of bright
light sources, creating a glowing effect. Depth of field adds a
sense of depth by blurring objects outside the focus range.
Motion blur simulates the blurring of fast-moving objects,
enhancing the sense of speed. Colour grading adjusts the
colours and contrast of the final image, creating a specific
mood or style. These effects can add a cinematic quality to the
rendering, making scenes more visually striking.
Custom Shader Support
Allowing users to write and integrate custom shaders
provides greater flexibility and control over the rendering
process. Shaders are small programs that run on the GPU and
132 ......................................................................................... Coding Mastery: Rendering Realities
define how vertices and pixels are processed. By supporting
custom shaders, the engine can accommodate a wide range of
visual styles and effects, from realistic materials and lighting
to stylized and abstract visuals. This feature empowers users
to push the boundaries of what the engine can render,
enabling unique and innovative graphics.
Level Editor
Creating a level editor for designing and editing scenes within
the engine makes it more accessible for content creators. A
level editor provides a visual interface for placing objects,
adjusting properties, and configuring scenes without writing
code. This tool can include features like drag-and-drop object
placement, real-time preview, and undo/redo functionality. A
level editor streamlines the creation process, making it easier
for artists and designers to build and iterate on scenes. It
enhances productivity and opens up the engine to a broader
audience.
Optimizations
Implementing various optimizations, such as frustum culling,
level of detail (LOD) management, and occlusion culling, can
improve performance for large and complex scenes. Frustum
culling discards objects outside the camera’s view, reducing
the number of objects to render. LOD management adjusts the
detail level of objects based on their distance from the camera,
Sujal Choudhari..................................................................................................................................... 133
rendering less detailed versions for distant objects. Occlusion
culling skips rendering objects blocked by other objects. These
optimizations ensure that the engine runs efficiently, even
with large and detailed scenes, maintaining a smooth and
responsive experience.
Cross-Platform Compatibility
We have used python and OpenGL already makes It Cross
platform, but you might have used OS dependent libraries to
add more advanced features, or help you as a tool. Ensuring
the engine runs smoothly on various platforms, including
Windows, macOS, Linux, and potentially mobile platforms,
broadens its usability. Cross-platform compatibility involves
handling different operating systems, hardware
configurations, and input methods. This capability allows
developers to target multiple platforms with a single
codebase, increasing the engine’s reach and potential user
base. It also involves optimizing performance and memory
usage for different devices, ensuring a consistent experience
across platforms.
Documentation and Tutorials
Developing comprehensive documentation and tutorials helps
users understand and utilize the engine effectively.
Documentation should cover all aspects of the engine, from
installation and setup to advanced features and customization.
134 ......................................................................................... Coding Mastery: Rendering Realities
Tutorials provide step-by-step guidance on common tasks,
helping users get started and build their skills. Good
documentation and tutorials reduce the learning curve,
making the engine more accessible to new users and
supporting advanced users in leveraging its full capabilities.
Community and Plugin System
Building a community around the engine allows developers to
share plugins, extensions, and resources, fostering a
collaborative ecosystem. A plugin system enables users to
extend the engine’s functionality with custom modules, tools,
and assets. This system encourages innovation and
experimentation, allowing users to tailor the engine to their
specific needs. A vibrant community provides support,
feedback, and inspiration, driving the engine’s development
forward. It creates a dynamic environment where users can
learn from each other and contribute to the engine’s growth.
Integration with External Tools
Providing integration with popular external tools like Blender,
Maya, and Unity allows for seamless asset import/export and
workflow integration. This capability ensures that users can
leverage their existing tools and pipelines while using the
engine. For example, models created in Blender can be easily
imported into the engine, or scenes can be exported to Unity
for further development. Integration with external tools
Sujal Choudhari..................................................................................................................................... 135
streamlines the workflow, making it easier to incorporate the
engine into existing projects and pipelines.
AI-Driven Enhancements
Exploring the use of machine learning for tasks such as texture
generation, procedural content creation, and intelligent NPC
behaviours can bring innovative capabilities to the engine.
Machine learning algorithms can analyse and generate
textures based on patterns and styles, creating high-quality
assets automatically. Procedural content generation can be
enhanced with AI, producing more varied and complex
environments. Intelligent NPC behaviours driven by AI can
adapt to player actions and create more engaging interactions.
These AI-driven enhancements can push the boundaries of
what the engine can achieve, opening up new possibilities for
creativity and realism.
Augmented Reality (AR) Support
Extending the engine to support AR applications enables real-
world interaction and overlay of digital content. AR support
involves rendering virtual objects in the real world, tracked by
devices like smartphones and AR glasses. This capability
allows users to interact with virtual objects in their physical
environment, creating immersive and interactive experiences.
AR applications range from gaming and education to
industrial training and visualization. Implementing AR
136 ......................................................................................... Coding Mastery: Rendering Realities
support can expand the engine’s applicability and open up
new avenues for innovation.
These potential future developments can significantly expand
the capabilities and applications of the 3D rendering engine,
making it a powerful tool for a wide range of uses, from
gaming and simulation to virtual reality and augmented
reality experiences. Each enhancement brings new
possibilities and challenges, pushing the boundaries of what
the engine can achieve and offering exciting opportunities for
developers and users alike.
Sujal Choudhari..................................................................................................................................... 137
138 ......................................................................................... Coding Mastery: Rendering Realities
Sujal Choudhari..................................................................................................................................... 139
140 ......................................................................................... Coding Mastery: Rendering Realities
Sujal Choudhari..................................................................................................................................... 141