AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Introduction to Computer Graphics and 3D Rendering

📚 Graphics⏱️ 24 min read🎓 Grade 9
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 24 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

A Stadium That Doesn't Exist

Open a cricket game on a phone and watch the broadcast-style camera swing around the stadium as the bowler starts his run-up. The grass looks flat and green, the stands curve away into the distance, and the boundary rope bends around the field exactly the way it would on television. It feels like you are looking at a real place. You are not. Inside the phone's memory there is no stadium, no grass, no crowd — there is a list of numbers describing corners of triangles, and a piece of software whose entire job is to turn those numbers into the colored dots on your screen, sixty times every second. That translation — from numbers describing a 3D scene to pixels lighting up a flat screen — is what this chapter is about. It is also, historically, one of the hardest problems computer scientists have solved, and the same mathematics is used whether the "scene" is a cricket stadium, a Chandrayaan lander approaching the lunar surface in an ISRO simulation, or a building an architect is showing a client before it is built.

What a Screen Actually Stores: The Pixel Grid

Before asking how 3D scenes get drawn, it helps to be precise about what "drawn" even means for a computer. Every screen — a phone, a laptop, a stadium's giant replay screen — is a rectangular grid of tiny dots called pixels (short for "picture elements"). A common laptop screen might be 1920 pixels wide and 1080 pixels tall, giving 1920 × 1080 = 2,073,600 individual pixels. Each pixel can only do one thing: glow in a single, solid color.

A color is stored as three numbers — the intensity of Red, Green, and Blue light, each usually ranging from 0 to 255 (a byte's worth of values, since 28 = 256). Pure red is (255, 0, 0); white, where all three colors are at full strength, is (255, 255, 255); black, where none are lit, is (0, 0, 0). A warm sunset orange might be (255, 140, 40). This RGB triplet, one per pixel, is stored in a block of memory called the framebuffer — literally the array of numbers that the screen reads, row by row, to know what to light up. Rendering a 3D scene, at the most basic level, means: figure out what RGB value belongs in every single cell of this grid.

Pixel positions themselves follow a convention that trips up almost every beginner. In ordinary math class, the Cartesian plane has its origin (0, 0) at the bottom-left, and y increases as you go up. Pixel grids do the opposite: the origin (0, 0) is the top-left corner of the screen, and the row number y increases as you go down. Column 0, row 0 is the top-left pixel; column 1919, row 1079 is the bottom-right pixel of a 1920×1080 screen. This is not a mathematical necessity — it is a historical convention, carried over from how old cathode-ray-tube displays were scanned, line by line, starting from the top. Every graphics program has to remember this flip somewhere, and forgetting it is a classic bug: shapes that should appear at the top of the screen show up at the bottom instead, because the y-axis was assumed to point the "normal" way.

Two Different Coordinate Systems, One Rendering Job

A 3D scene — a cube, a cricket ball, a stadium — is described using three coordinates per point: x (left-right), y (up-down), and z (near-far, i.e. depth). This is called world space, and it describes where things actually are, independent of any viewer. A cricket ball's true position doesn't change depending on where the camera is standing.

The final image, on the other hand, lives in the 2D pixel grid described above — every point on screen has only a column and a row, no depth. Rendering is the process of converting a 3D world-space description into a 2D grid of colored pixels, as seen from one particular viewpoint (the "camera" or "eye"). The single hardest and most important step in that process — the one that makes an image look three-dimensional rather than flat — is called projection: converting a 3D point (x, y, z) into a 2D point on the image.

The Intuition Before the Formula: Converging Railway Tracks

Before writing any equation, look at something every Indian student has seen from a train window or a station platform: two parallel railway tracks. In reality, the two rails never get closer together — by law and by physical necessity, Indian Railways' broad-gauge tracks are 1.676 metres apart everywhere along their length. Yet if you stand on the platform and look down the line, the tracks visibly converge toward a single point on the horizon. A pillar of a station roof that is close to you looks tall; an identical pillar far down the platform looks tiny, even though both pillars are the same height in reality.

This is perspective: things that are farther from your eye occupy a smaller angle of your vision, and so they appear smaller and closer together on whatever surface you're viewing them on — your retina, a camera's film, or a computer screen. Any 3D rendering system that wants to look realistic must reproduce this shrinking-with-distance effect mathematically. That is exactly what the projection formula we are about to derive does.

The Pinhole Camera Model

The simplest way to model an eye or a camera mathematically is the pinhole camera: imagine a box that is completely dark inside, with one tiny hole on the front face. Light from the outside world passes through that single hole and lands on the back wall of the box, forming an upside-down image. Two facts about this setup matter for us: first, every ray of light that reaches the back wall passed through exactly one point — the pinhole — on its way there. Second, the image forms on a flat surface at some fixed distance behind the hole.

To build our model, we place the eye (the pinhole) at the origin of our coordinate system, (0, 0, 0), looking straight down the positive z-axis. Instead of putting the image surface behind the eye as a real pinhole camera does (which flips the image upside down), computer graphics conventionally puts an imaginary flat image plane — think of it as a pane of glass, or the screen itself — a fixed distance d in front of the eye, at z = d. Every object we will render sits farther from the eye than this plane, at some depth z > d. The question we now need to answer is: given a 3D point at (x, y, z) somewhere out in the scene, exactly where on that image plane does it appear?

Deriving the Projection Formula: Similar Triangles

This is the single most important idea in this chapter, so we will build it one labeled point at a time. To keep the picture simple, we work in a side view — the x-z plane, looking at the scene from directly above, ignoring y for now (the y-coordinate works out by an identical argument, by symmetry).

We need three distinct points to form a triangle around our 3D point P:

  • E — the eye, at the origin (0, 0).
  • A — the point (0, z) on the depth axis, directly below P: it has the same depth z as P, but zero horizontal offset. A is the "foot" you get by dropping a perpendicular straight down from P onto the axis.
  • P — our 3D point itself, at (x, z), sitting a horizontal distance x away from the axis.

These three distinct points E, A, and P form a right triangle, with the right angle sitting at A (because AP is drawn perpendicular to the axis EA). The leg EA runs along the depth axis and has length z. The leg AP runs perpendicular to the axis and has length x. The hypotenuse EP is the actual line of sight — the ray of light — from the eye to the point.

Now look at where that same line of sight, ray EP, crosses the image plane at z = d. Call that crossing point S — this is precisely the projected point we are trying to find, at coordinates (sx, d). Let B be the foot of the perpendicular from S onto the axis, at (0, d). Once again we have three distinct points, E, B, and S, forming a second right triangle, with the right angle at B: leg EB has length d, leg BS has length sx.

Because S lies exactly on segment EP, the small triangle E-B-S sits inside the big triangle E-A-P, sharing the same angle at vertex E. Two right triangles that share an acute angle are similar — same shape, different size — so their corresponding sides are in the same ratio:

BS / EB  =  AP / EA
  sx / d  =  x / z

Solving for sx:

sx = (d / z) * x

and by the identical argument applied to the vertical direction,

sy = (d / z) * y

This pair of formulas is the mathematical heart of 3D rendering. Notice what it says: the projected position is the original coordinate, scaled by d/z — the image-plane distance divided by the point's depth. As z grows larger (the point moves farther away), d/z shrinks toward zero, so sx shrinks toward zero too: distant things project closer to the center of the image and occupy less space, exactly like the far pillar on the railway platform looking tiny. As z shrinks toward d (the point approaches the image plane), d/z approaches 1, and sx approaches x itself — the point is projected almost exactly onto itself, which makes sense, since it's practically touching the plane.

z-axis (depth) image plane (z = d) z d E (0,0) eye A (0,z) P (x,z) — 3D point B (0,d) S (sx,d) — projected point x sx

Worked Numeric Example: Projecting a Cube

Let's use real numbers. Suppose the image plane sits at d = 5, and a small cube floats in the scene with its near face at z = 6 and its far face at z = 8 — both faces are farther from the eye than the image plane, so the pinhole-camera picture stays physically consistent: the cube is genuinely "out there" beyond the glass, not somehow behind it. Let the cube run from x = −1 to x = 1 and y = −1 to y = 1 on both faces, so it's a 2×2×2 cube.

Here is a short program that computes the projected position of each corner:

def project(x, y, z, d):
    sx = d * x / z
    sy = d * y / z
    return sx, sy

d = 5
near_face = [(-1, -1, 6), (1, -1, 6), (1, 1, 6), (-1, 1, 6)]
far_face  = [(-1, -1, 8), (1, -1, 8), (1, 1, 8), (-1, 1, 8)]

for x, y, z in near_face:
    print(project(x, y, z, d))

Tracing the loop by hand for the first corner (x=−1, y=−1, z=6): sx = 5 × (−1) / 6 = −0.833, sy = 5 × (−1) / 6 = −0.833, so project returns (−0.833, −0.833). Running the same formula across every corner gives:

Near face (z = 6): corners project to x = ±0.833, y = ±0.833
Far face  (z = 8): corners project to x = ±0.625, y = ±0.625

Look at what just happened: the near face, only 1 unit of depth closer to the eye than the far face, projects to a span of 0.833 on each side of center, while the far face projects to only 0.625. The near face will draw as the visibly larger square on screen — exactly the foreshortening effect we expect from a cube receding into the distance, and it fell straight out of dividing by z, with no special-case "make far things smaller" rule anywhere in the code.

From Projected Coordinates to Actual Pixels

The sx, sy values we just computed are not yet pixel positions — they are coordinates on the image plane, typically ranging roughly from −1 to 1 for points inside the camera's field of view. A final step, the viewport transform, stretches this −1-to-1 range across the actual pixel grid, and — remembering our earlier warning about pixel rows increasing downward while y in a scene increases upward — flips the vertical direction:

def to_pixel(sx, sy, width, height):
    px = (sx + 1) / 2 * width
    py = (1 - sy) / 2 * height
    return px, py

Tracing this for the near face's right edge, sx = 0.833, on an 800-pixel-wide screen: px = (0.833 + 1) / 2 × 800 = (1.833 / 2) × 800 = 0.9165 × 800 ≈ 733. Its left edge, sx = −0.833, gives px = (0.167 / 2) × 800 ≈ 67. The near face spans about 733 − 67 = 666 pixels of screen width. Repeating this for the far face (sx = ±0.625) gives px = (1.625 / 2) × 800 = 650 and px = (0.375 / 2) × 800 = 150, a span of only 500 pixels. The near face genuinely occupies more of the screen — 666 pixels versus 500 — which is the numeric, pixel-level confirmation of the foreshortening we predicted algebraically.

Two Misconceptions Worth Correcting

A very common misconception is that a 3D graphics program scales distant objects down using some artistic or hand-tuned rule — as if a programmer wrote "if far, shrink by some amount." That is not what happens. Shrinking-with-distance is not designed in; it is a mathematical consequence of dividing by z in the projection formula. Nobody tells the far face of the cube to look smaller — it simply falls out of sx = dx/z once z is larger.

A second common misconception is that a 3D scene stored in a computer is some kind of photograph or bitmap of the object, the way a phone camera stores a picture. It is not. A 3D model is a list of coordinates (vertices) and rules for connecting them into flat triangles (a mesh), plus material and color information. No image exists until the projection and rasterization steps described in this chapter run, for one specific eye position, at render time. Rotate the camera and an entirely new image is computed from the very same stored coordinates — nothing about the model itself needed to change.

The Rest of the Pipeline: Rasterization and Depth

Projection tells us where the three corners of a triangle land on the image plane, but a triangle is a solid region, not just three points. Rasterization is the step that figures out exactly which pixels fall inside that projected triangle and colors them in — walking across the pixel grid row by row and testing, for each pixel, whether its center lies inside the triangle's three edges.

A scene almost always has multiple triangles whose projections overlap on screen — a bowler's arm in front of his own body, or a cricket ball in front of the boundary rope. The renderer needs to know, for every pixel, which overlapping triangle is actually closest to the eye and should "win" that pixel. The standard solution is a depth buffer (or z-buffer): alongside the RGB framebuffer, the renderer keeps a second grid, the same size, that stores the depth (z-value) of the nearest surface drawn so far at each pixel. When a new triangle is rasterized, each of its pixels is only drawn if its z-value is smaller (nearer) than whatever is already recorded at that pixel's depth-buffer entry; if it is nearer, both the color and the depth-buffer entry are updated. This is exactly how a broadcast graphic like a virtual boundary-rope overlay is drawn behind the fielders but in front of the grass in a cricket telecast — depth values, not drawing order, decide who's in front.

Storing the Final Image: The Framebuffer and File Size

Once rasterization and depth-testing finish, the framebuffer holds a complete grid of RGB triplets — the finished image. We can now compute exactly how much memory a single uncompressed frame at Full HD resolution requires. A 1920×1080 screen has 1920 × 1080 = 2,073,600 pixels. Each pixel stores 3 bytes — one byte (8 bits, values 0–255) for each of red, green, and blue. That's 2,073,600 × 3 = 6,220,800 bytes per frame. Using the decimal convention common for file and network sizes, where 1 MB is defined as 1,000,000 bytes, that works out to 6,220,800 ÷ 1,000,000 ≈ 6.22 MB — for a single frame. A game rendering 60 of these every second, uncompressed, would need to move over 373 MB of pixel data per second, which is exactly why real-time graphics hardware (the GPU) is built specifically to do this projection-rasterization-depth-test cycle in dedicated, massively parallel hardware rather than as ordinary software running on a general-purpose CPU.

Check Your Understanding

  • A point sits at (x, z) = (2, 4) in world space, with the image plane at d = 2. Compute sx using sx = dx/z, and explain in one sentence why the answer is smaller than x.
  • Two points share the same depth z = 10, but one has x = 1 and the other x = 3. Which one projects farther from the center of the image, and why does that match how the similar-triangles ratio sx/d = x/z behaves?
  • Explain, using the E-A-P and E-B-S triangles from this chapter, why moving the image plane farther away (increasing d, keeping the point's x and z fixed) makes sx larger. Does this match what "zooming in" a camera should do?
  • A screen is 1280×720 pixels. Using 3 bytes per pixel and the decimal MB convention (1 MB = 1,000,000 bytes) from this chapter, calculate the framebuffer size in bytes and megabytes.
  • Two triangles in a scene project to overlapping pixels on screen. Name the buffer that decides which triangle's color actually gets drawn at each overlapping pixel, and describe in one sentence what value it compares.

Quick answers to check yourself: (1) sx = 2×2/4 = 1, smaller than x=2 because d/z = 2/4 = 0.5 shrinks it. (2) x=3 projects farther out, since sx/d = x/z means sx is directly proportional to x when z is fixed. (3) sx = dx/z grows as d grows because sx is directly proportional to d — this matches a camera "zooming in," which effectively pushes the image plane farther from the eye, magnifying everything on it. (4) 1280×720 = 921,600 pixels × 3 bytes = 2,764,800 bytes ≈ 2.76 MB. (5) The depth buffer (z-buffer); it compares the depth (z-value) of the new pixel against the nearest depth already recorded for that pixel.

Summary

  • A screen is a grid of pixels; each pixel stores an RGB color as three byte values (0–255), held together in the framebuffer.
  • Pixel coordinates start at the top-left and grow downward — the opposite of the Cartesian y-axis from math class — a flip that every renderer must handle explicitly.
  • 3D scenes are described in world-space coordinates (x, y, z); rendering converts that description into 2D pixel colors as seen from one eye position.
  • Perspective projection places the eye at the origin and an image plane at distance d; similar triangles (E, A, P and E, B, S — three genuinely distinct points in each) give sx = dx/z and sy = dy/z.
  • Because sx and sy are divided by z, points farther away automatically project closer to the center and occupy less of the image — foreshortening is a consequence of the formula, not a separately coded rule.
  • Projected coordinates are mapped onto actual pixels by the viewport transform, which also flips the vertical axis to match the pixel grid's top-left origin.
  • Rasterization fills in which pixels belong to each projected triangle; a depth buffer resolves which overlapping triangle is nearest the eye at every pixel.
  • 3D models store vertices and connectivity, not photographs — the actual image is computed fresh, from projection through rasterization, every time the camera or scene changes.

Practice Exercises

Now it is time to practice! Complete these challenges to solidify your understanding:

  • Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
  • Exercise 2: Find a real-world example where introduction to computer graphics and 3d rendering is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
  • Exercise 3: Create a mind-map connecting introduction to computer graphics and 3d rendering to at least 3 other topics you have studied.
← Recommendation Systems: How Netflix, Spotify, and Flipkart Know What You WantComputer Vision for Self-Driving Cars →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn