Live
Black Hat USAAI BusinessBlack Hat AsiaAI BusinessOpenAI closes larger than expected funding round of $122bnSilicon RepublicIran threatens attacks on Nvidia, Microsoft, Intel, and other US tech firms in the Middle EastTechSpotAI tools are great for individuals. but what about your team?DEV CommunityOpenAI: We’re generating $2 billion a month - thestack.technologyGoogle News: OpenAIBeyond Human Wisdom: Can Humanity Survive the Rise of AGI?LessWrong AICreate a workspace scheduler using Bryntum Scheduler Pro and MongoDBDEV CommunityNvidia commits billions to Lumentum, Synopsys, Nokia, XAI, OpenAI, Intel in March alone - 24/7 Wall St.Google News: OpenAIDiscover a Free AI Voice Tool with Emotional Control for Content CreatorsDEV CommunitySeatGeek launches its app in ChatGPT - IQ MagazineGoogle News: ChatGPTI tested denim jackets from Banana Republic, Old Navy, and Gap. One became my new closet staple.Business InsiderReact 20 Is Coming. Here's What Actually Matters (and What Doesn't).DEV CommunityAsync/Await in JavaScript: Writing Cleaner Asynchronous CodeDEV CommunityBlack Hat USAAI BusinessBlack Hat AsiaAI BusinessOpenAI closes larger than expected funding round of $122bnSilicon RepublicIran threatens attacks on Nvidia, Microsoft, Intel, and other US tech firms in the Middle EastTechSpotAI tools are great for individuals. but what about your team?DEV CommunityOpenAI: We’re generating $2 billion a month - thestack.technologyGoogle News: OpenAIBeyond Human Wisdom: Can Humanity Survive the Rise of AGI?LessWrong AICreate a workspace scheduler using Bryntum Scheduler Pro and MongoDBDEV CommunityNvidia commits billions to Lumentum, Synopsys, Nokia, XAI, OpenAI, Intel in March alone - 24/7 Wall St.Google News: OpenAIDiscover a Free AI Voice Tool with Emotional Control for Content CreatorsDEV CommunitySeatGeek launches its app in ChatGPT - IQ MagazineGoogle News: ChatGPTI tested denim jackets from Banana Republic, Old Navy, and Gap. One became my new closet staple.Business InsiderReact 20 Is Coming. Here's What Actually Matters (and What Doesn't).DEV CommunityAsync/Await in JavaScript: Writing Cleaner Asynchronous CodeDEV Community

3D Shadow Mapping Renderer in JavaScript

Nicholas Carlini BlogAugust 12, 20191 min read0 views
Source Quiz

Late last year I decided it would be fun to build a 3D renderer in JavaScript. Recently it got into some sort of finished state and decided to put it here. This isn't so much of a tutorial on how to get there, but rather more of a here's a fun thing to do with nice pictures. But it was interesting to do. So here's that.

Late last year I decided it would be fun to build a 3D renderer in JavaScript. Recently it got into some sort of “finished” state and decided to put it here. This isn't so much of a tutorial on how to get there, but rather more of a “here's a fun thing to do with nice pictures”. But it was interesting to do. So here's that.

START

To start out I really wanted to do this from first principles and didn't want to “cheat” by using any kind of libraries. Initially, that meant writing the rendering engine entirely with the 2d graphics library, doing all of the 3d math by hand.

Technically there's nothing terribly interesting going on with this initial renderer. It's just over 250 lines long and does the naive thing: for each triangle in the three dimensional space, the renderer projects it onto the two dimensional screen, drawing from the back to the front.

Smooth surface:

START

Once I actually got that working doing all of the math by hand in JavaScript I decided that using WebGL would probably be worth it, and actually probably wasn't cheating all that much. WebGL exposes access to the GPU-enabled rendering engine through JavaScript. While it does abstract away some of the rendering, it's less than I thought---it just supports the ability to do the necessary math efficiently---so I decided this wouldn't be cheating. And fortunately, it didn't take long to reproduce the initial renderer, but this time supporting much better (and more efficient) graphics.

Shadow map resolution:

256

START

Once the base renderer was ready, I started adding lighting with shadows. To do this I used shadow mapping: one of the most common approaches for generating real-time shadows. At a high level, shadow mapping works by rendering the scene twice every frame. First, from the perspective of the light source, we render the scene and record how far the closest object is to the light, for each pixel in the scene. This gives us the “shadow map” Then, we render the scene again, this time from the actual camera. In order to determine if any given pixel is in light or is in the shadow, we project it back onto the (pre-rendered) shadow map. If this pixel is the closest one to the light, then it's in light; if not, it's in the shadow.

Unfortunately, the light is fairly blocky.

Getting a high-quality shadow on my old laptop without a dedicated GPU wasn't possible: I had to increase the dimensions of the shadow camera to 2048 by 2048, and I was no longer getting a smooth 60 frames a second. Also, those hard edges on the shadows really don't look that realistic.

Shadow map resolution:

256

START

In order to get nicer-looking shadows efficiently, I decided to move to a slightly fancier method: variance shadow maps. At their core they do the same thing as standard shadow maps: first, render from the light, recording the distance to the light for each pixel; then, render from the camera. However, variance shadow maps introduce one key difference: on the first rendering pass, instead of just recording the distance from the object to the camera, also record the distance squared.

But first, let's back up---one of the main reason that standard shadow maps look bad is because of their hard edges. It would be possible to blur the edges slightly by taking a large number of random samples when rendering from the camera, but this doesn't look great and is very inefficient.

Variance shadow maps allow using Chebyshev's inequality to get a nice smooth blur by estimating the fraction of pixels that are occluded by comparing the squared expected distance to the expected distance squared. The math is somewhat complicated here, but when you do it right, you get a pretty picture.

At the same time, I also added some light bloom, the effect that extra-bright lights cause a fuzzy blur around the boundary. Given that I had already written a blur method for computing the shadow maps, adding a filter to the screen was straightforward.

And that's all there is.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by AI News Hub · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

Knowledge Map

Knowledge Map
TopicsEntitiesSource
3D Shadow M…Nicholas Ca…

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 213 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!

More in Products