Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» CS 488: Lecture 15 – Extra Extra Texturing

teaching machines

CS 488: Lecture 15 – Extra Extra Texturing

March 17, 2021 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
graphics-3d, lectures, spring-2021.

Dear students:

Today we visit just a few more miscellaneous topics related to texturing. We’ll examine texturing a cube, dealing with constraints on a texture’s dimensions, and apply textures to produce more discrete shading.

Texture Atlas

Texturing a quadrilateral is like biking down hill. You don’t have to exert much effort to paste a rectangular texture on a quadrilateral. We must try to texture something more difficult, like a cube. Suppose we want to put a different image on each face. Our first impulse might be to use six textures, but it’s not clear how we would apply different textures to different faces within a single draw call. We either need the faces to be separated or we need to use only a single texture.

When we load multiple images into a single texture, we have a texture atlas. For a cube, we can use an atlas that is a 4×2 grid of square cells, filling 6 of the cells with the cube’s images. The texture coordinates must be carefully assigned so that each face maps to the right “page” of the atlas.

Powers of 2

Some graphics libraries expect textures to have dimensions that are powers of 2, like 1024×256. We reap several advantages by constraining textures in this way. First, we are guaranteed to have a clean mipmap set since the dimensions will divide by two with no remainder. Second, texture lookups may be faster. Let’s explore a few reasons why that might be.

What is 5 << 1? In binary, that’s 101 << 1, which is $1010_2$ or $10_{10}$. Extending this, 5 << 2 is $10100_2$ or $20_{10}$. Shifting binary digits is the same multiplying by 2. Hang on to that truth for a second.

If you’ve got integer texture coordinates s and t and width w, then you can turn a 2D texel coordinate into a 1D index into a row-major buffer using these equations:

i = w * t + s

However, if w is 2 to the power of n, then the multiplication can be replaced by shifting:

i = t << n + s

Shifting in hardware is generally faster to perform than multiplication.

WebGL allows non-power of 2 textures (NPOT), but a call to gl.generateMipmap will fail on such textures. The minification filter must not be set to any interpolation based on mipmap levels. Only gl.LINEAR and gl.NEAREST are supported.

Additionally, one cannot use gl.REPEAT or gl.MIRRORED_REPEAT for wrapping coordinates on NPOT textures. Only gl.CLAMP_TO_EDGE is supported. Let’s explore why this might be. Suppose we have a texture that is 64 texels wide. We have these binary representations of texture coordinate 50 and its successive counterparts in the repetitions that will reduce to 50:

50          =   110010
50 + 64 * 1 =  1110010
50 + 64 * 2 = 10110010
50 + 64 * 3 = 11110010

We see from these examples that we can reduce the out-of-range texture coordinates down to an in-range texture coordinate with a masking operation. In this case, we want to mask with bits 111111, which is 63 in decimal. In general, we calculate our in-range coordinate like this:

texcoord = texcoord & (width - 1)

Computing the mask only works when the size is a power of 2, since one less than a power of 2 has all its bits on.

If you have an NPOT texture, you have several options:

  1. Live with it. The graphics API may accept NPOT textures.
  2. Pad the image to the nearest higher powers of 2 in your graphics editor. The padding wastes disk and increases download times.
  3. Pad the image to the nearest higher powers of 2 programmatically at runtime. JavaScript’s Image class doesn’t give us much control over pixels. However, its canvas element does. We can write this code to draw the NPOT image into a POT canvas:
    function padToPot(image) {
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
      canvas.width = powerOfTwoCeiling(image.width);
      canvas.height = powerOfTwoCeiling(image.height);
      context.drawImage(image, 0, 0);
      return context.getImageData(0, 0, canvas.width, canvas.height);
    }
    
  4. Allocate a texture on the GPU whose dimensions are the nearest higher powers of 2 and upload the texels as a sub-image within the texture.
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, powerOfTwoCeiling(image.width), powerOfTwoCeiling(image.height), 0, gl.RGBA, gl.UNSIGNED_BYTE,
    gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, gl.RGBA, gl.UNSIGNED_BYTE, image);
    

The code in these solutions depends on a powerOfTwoCeiling function. We compute the power of 2 that’s greater than or equal to a number, by taking the base-2 logarithm, rounding up, and raising 2 to that power. The function can be written as a gauntlet of math functions:

function powerOfTwoCeiling(x) {
  return Math.pow(2, Math.ceil(Math.log2(x)));
}

Alternatively, if you prefer to overengineering things, there are bit twiddling hacks that can be used to find the leftmost 1-bit, which gets you pretty close to the power of 2.

With the padding options, you must adapt the texture coordinates to the new resolution.

Someday these limitations on NPOT textures may go away. The practical concerns that led to these limitations might not even be relevant on modern hardware, yet we continue to abide by them for backward compatibility.

Toon Shading

The Blinn-Phong lighting model that we’ve implemented produces smooth shading. In some games and movies, we see a different kind of lighting that uses a small number of discrete bands of illumination. This effect is sometimes called toon shading or cel shading because it mimics the practice of some cartoon animators who painted the animated foreground on sheets of celluloid. Celluloid is transparent and can be overlaid on static and painterly backgrounds. Many cels were needed to orchestrate the animation, and animators saved time by using few colors and no gradients.

In standard diffuse shading, we modulate the surface’s albedo according to the degree of alignment between the normal and the light vector. The dot product—the cosine—that we use to compute this alignment produces a continuous dropoff. To get a discrete dropoff, we can use the dot product as a texture coordinate into a 1D lookup table that gives a small set of “litness” values.

Unlike the full OpenGL, WebGL doesn’t allow 1D textures. But we can create a 2D texture that has a height of 1. We could make our lookup table in an image editor, but it’s also possible to synthesize it programmatically:

function loadTable() {
  // Make an array of unsigned bytes with a handful of illumination levels.
  const table = new Uint8Array(128);
  for (let i = 0; i < table.length; i += 1) {
    if (i < 20) {
      table[i] = 0;
    } else if (i < 30) {
      table[i] = 50;
    } else if (i < 70) {
      table[i] = 128;
    } else if (i < 120) {
      table[i] = 200;
    } else {
      table[i] = 255;
    }
  }

  // Upload the texture.
  gl.activeTexture(gl.TEXTURE0);
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, table.length, 1, 0, gl.RED, gl.UNSIGNED_BYTE, table);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.generateMipmap(gl.TEXTURE_2D);

  return texture;
}

We change our fragment shader to index into this shader to determine the litness level:

uniform sampler2D table;

const vec3 lightDirection = normalize(vec3(1.0, 1.0, 1.0));
const vec3 albedo = vec3(1.0, 1.0, 1.0);

in vec3 fnormal;

out vec4 fragmentColor;

void main() {
  vec3 normal = normalize(fnormal);
  float litness = max(0.0, dot(normal, lightDirection));
  float level = texture(table, vec2(litness, 0.0)).r;
  fragmentColor = vec4(albedo * level, 1.0);
}

TODO

Here’s your TODO list:

See you next time.

Sincerely,

P.S. It’s time for a haiku!

Computers on Mars
They’ll use binary for sure
Because it’s base 2