WebGL demos now decode PNG in C. (#214)

This removes the trick of drawing to a 2D canvas. It was corrupting the
alpha in RGBM images, which caused subtle banding in the Suzanne
reflection. Moreover it added complexity to the demo code.

This commit will increase the pre-zipped WASM size by 17k, but I think
it's worth it.

This also removes one of the cmgen passes in the WebGL build; we were
using PNG instead of RGBM for the skybox to work around the banding
issue that this fixes.
This commit is contained in:
Philip Rideout
2018-09-06 13:48:22 -07:00
committed by GitHub
parent db79bc9729
commit 7b36ed4638
4 changed files with 33 additions and 82 deletions

View File

@@ -265,6 +265,7 @@ endif()
set (FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
if (WEBGL)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${FILAMENT}/samples/web)
endif()

View File

@@ -77,27 +77,21 @@ add_custom_command(
DEPENDS filamesh)
# Next build two cubemaps:
# (1) single miplevel of blurry skybox (png)
# (2) all miplevels for the IBL (rgbm)
# (1) single miplevel of blurry skybox
# (2) all miplevels for the IBL
set(source_envmap "${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/environments/syferfontein_18d_clear_2k.hdr")
foreach(FACE nx ny nz px py pz)
set(target_skybox ${target_skybox} public/syferfontein_18d_clear_2k/${FACE}.png)
set(target_skybox ${target_skybox} public/syferfontein_18d_clear_2k/${FACE}.rgbm)
foreach(MIP 0 1 2 3 4 5 6 7 8)
set(target_envmap ${target_envmap} public/syferfontein_18d_clear_2k/m${MIP}_${FACE}.rgbm)
endforeach()
endforeach()
add_custom_command(
OUTPUT ${target_envmap}
COMMAND cmgen -x public --format=rgbm --size=256 ${source_envmap}
MAIN_DEPENDENCY ${source_envmap}
DEPENDS cmgen)
add_custom_command(
OUTPUT ${target_skybox}
COMMAND cmgen -x public --format=png --size=512 --extract-blur=0.1 ${source_envmap}
OUTPUT ${target_skybox} ${target_envmap}
COMMAND cmgen -x public --format=rgbm --size=512 --extract-blur=0.1 ${source_envmap}
MAIN_DEPENDENCY ${source_envmap}
DEPENDS cmgen)
@@ -126,7 +120,7 @@ function(add_demo NAME)
filaweb.h
filaweb.cpp)
add_dependencies(${NAME} sample_materials sample_assets)
target_link_libraries(${NAME} PRIVATE filament math filamat utils)
target_link_libraries(${NAME} PRIVATE filament math utils stb)
# Copy the generated js and wasm files into the public folder, as well as the app-specific
# manually-written HTML file.

View File

@@ -19,6 +19,11 @@
#include <string>
#include <sstream>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_STDIO
#define STBI_ONLY_PNG
#include <stb_image.h>
using namespace filament;
using namespace std;
@@ -56,31 +61,20 @@ Asset getRawFile(const char* name) {
}
Asset getTexture(const char* name) {
// Obtain image dimensions from JavaScript.
uint32_t dims[2];
EM_ASM({
var dims = $0 >> 2;
var name = UTF8ToString($1);
HEAP32[dims] = assets[name].width;
HEAP32[dims+1] = assets[name].height;
}, dims, name);
const uint32_t nbytes = dims[0] * dims[1] * 4;
// Move the data from JavaScript.
Asset result = getRawFile(name);
int width, height, ncomp;
stbi_info_from_memory(result.data.get(), result.nbytes, &width, &height, &ncomp);
const uint32_t nbytes = width * height * 4;
uint8_t* texels = new uint8_t[nbytes];
EM_ASM({
var texels = $0;
var name = UTF8ToString($1);
var nbytes = $2;
HEAPU8.set(assets[name].data, texels);
assets[name].data = null;
}, texels, name, nbytes);
stbi_uc* decoded = stbi_load_from_memory(result.data.get(), result.nbytes, &width, &height,
&ncomp, 4);
memcpy(texels, decoded, nbytes);
stbi_image_free(decoded);
return {
.data = decltype(Asset::data)(texels),
.nbytes = nbytes,
.width = dims[0],
.height = dims[1]
.width = uint32_t(width),
.height = uint32_t(height)
};
}
@@ -96,17 +90,6 @@ Asset getCubemap(const char* name) {
HEAP32[nmips] = assets[name].nmips;
}, &nmips, name, &prefix[0]);
// Obtain dimensions of a face from miplevel 0.
uint32_t dims[2];
EM_ASM({
var dims = $0 >> 2;
var name = UTF8ToString($1);
var face = UTF8ToString($2);
var key = assets[name].name + face;
HEAP32[dims] = assets[key].width;
HEAP32[dims+1] = assets[key].height;
}, dims, name, "m0_px.rgbm");
// Build a flat list of mips for each cubemap face.
Asset* envFaces = new Asset[nmips * 6];
for (uint32_t mip = 0, i = 0; mip < nmips; ++mip) {
@@ -130,12 +113,12 @@ Asset getCubemap(const char* name) {
string key = string(prefix) + suffix;
skyFaces[i++] = getTexture(key.c_str());
};
get("px.png");
get("nx.png");
get("py.png");
get("ny.png");
get("pz.png");
get("nz.png");
get("px.rgbm");
get("nx.rgbm");
get("py.rgbm");
get("ny.rgbm");
get("pz.rgbm");
get("nz.rgbm");
// Load the spherical harmonics coefficients.
Asset* shCoeffs = new Asset;
@@ -145,8 +128,8 @@ Asset getCubemap(const char* name) {
return {
.data = decltype(Asset::data)(),
.nbytes = 0,
.width = dims[0],
.height = dims[1],
.width = envFaces[0].width,
.height = envFaces[0].height,
.envMipCount = nmips,
.envShCoeffs = decltype(Asset::envShCoeffs)(shCoeffs),
.envFaces = decltype(Asset::envFaces)(envFaces),

View File

@@ -66,35 +66,8 @@ function load_rawfile(url) {
return promise;
}
let context2d = document.createElement('canvas').getContext('2d');
// This function does PNG decoding (or JPEG, or whatever), which allows us to avoid including a
// texture decoder (such as stb_image) in the WebAssembly module. It always returns RGBA8 data,
// regardless of how it is stored in the image file.
function load_texture(url) {
let promise = new Promise((success, failure) => {
let img = new Image();
img.src = url;
img.onerror = failure;
img.onload = () => {
context2d.canvas.width = img.width;
context2d.canvas.height = img.height;
context2d.width = img.width;
context2d.height = img.height;
context2d.globalCompositeOperation = 'copy';
context2d.drawImage(img, 0, 0);
var imgdata = context2d.getImageData(0, 0, img.width, img.height).data.buffer;
success({
kind: 'image',
name: url,
data: new Uint8Array(imgdata),
width: img.width,
height: img.height
});
}
});
return promise;
}
// The C++ layer uses STB to decode the PNG contents into RGBA data.
let load_texture = load_rawfile;
function load_cubemap(urlprefix, nmips) {
let promises = {};
@@ -107,7 +80,7 @@ function load_cubemap(urlprefix, nmips) {
}
}
for (let face of 'px nx py ny pz nz'.split(' ')) {
name = face + '.png';
name = face + '.rgbm';
promises[name] = load_texture(urlprefix + name);
}
let numberRemaining = Object.keys(promises).length;