Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for linear filtering of floating point textures. #6943

Merged
merged 5 commits into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions Source/Renderer/Context.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,15 @@ define([
this._blendMinmax = !!getExtension(gl, ['EXT_blend_minmax']);
this._elementIndexUint = !!getExtension(gl, ['OES_element_index_uint']);
this._depthTexture = !!getExtension(gl, ['WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture']);
this._textureFloat = !!getExtension(gl, ['OES_texture_float']);
this._textureHalfFloat = !!getExtension(gl, ['OES_texture_half_float']);
this._fragDepth = !!getExtension(gl, ['EXT_frag_depth']);
this._debugShaders = getExtension(gl, ['WEBGL_debug_shaders']);

this._textureFloat = !!getExtension(gl, ['OES_texture_float']);
this._textureHalfFloat = !!getExtension(gl, ['OES_texture_half_float']);

this._textureFloatLinear = !!getExtension(gl, ['OES_texture_float_linear']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anything need to be taken into account for WebGL 1 vs. 2?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming that the extension is still required for WebGL 2. I couldn't find anything in the spec that says it isn't allowed in WebGL 2; however, I do see the extension for OES_texture_float_linear under the WebGL 2 tab at webglreport.com. Also, I don't see the extension for OES_texture_half_float_linear in the WebGL 2 tab, but I do see it under the WebGL 1 tab.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to MDN it is both a WebGL 1 and 2 extension.
https://developer.mozilla.org/en-US/docs/Web/API/OES_texture_float_linear

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And OES_texture_half_float_linear is gone under WebGL 2 and it's included with OES_texture_float_linear. (Similar to what they did for floating-point color buffer attachments)

Copy link
Contributor

@lilleyse lilleyse Sep 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@bagnell bagnell Sep 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so. The WebGL 2 conformance tests have a check to make sure that extension isn't available in WebGL 2:
https://github.com/KhronosGroup/WebGL/blob/master/conformance-suites/2.0.0/conformance2/extensions/promoted-extensions.html#L59

this._textureHalfFloatLinear = !!getExtension(gl, ['OES_texture_half_float_linear']);

this._colorBufferFloat = !!getExtension(gl, ['EXT_color_buffer_float', 'WEBGL_color_buffer_float']);
this._colorBufferHalfFloat = !!getExtension(gl, ['EXT_color_buffer_half_float']);

Expand Down Expand Up @@ -564,7 +568,7 @@ define([
},

/**
* <code>true</code> if OES_texture_float is supported. This extension provides
* <code>true</code> if OES_texture_float is supported. This extension provides
* access to floating point textures that, for example, can be attached to framebuffers for high dynamic range.
* @memberof Context.prototype
* @type {Boolean}
Expand All @@ -577,7 +581,7 @@ define([
},

/**
* <code>true</code> if OES_texture_half_float is supported. This extension provides
* <code>true</code> if OES_texture_half_float is supported. This extension provides
* access to floating point textures that, for example, can be attached to framebuffers for high dynamic range.
* @memberof Context.prototype
* @type {Boolean}
Expand All @@ -589,6 +593,39 @@ define([
}
},

/**
* <code>true</code> if OES_texture_float_linear is supported. This extension provides
* access to linear sampling methods for minification and magnification filters of floating-point textures.
* @memberof Context.prototype
* @type {Boolean}
* @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/}
*/
textureFloatLinear : {
get : function() {
return this._textureFloatLinear;
}
},

/**
* <code>true</code> if OES_texture_half_float_linear is supported. This extension provides
* access to linear sampling methods for minification and magnification filters of half floating-point textures.
* @memberof Context.prototype
* @type {Boolean}
* @see {@link https://www.khronos.org/registry/webgl/extensions/OES_texture_half_float_linear/}
*/
textureHalfFloatLinear : {
get : function() {
return (this._webgl2 && this._textureFloatLinear) || (!this._webgl2 && this._textureHalfFloatLinear);
}
},

/**
* <code>true</code> if EXT_texture_filter_anisotropic is supported. This extension provides
* access to anisotropic filtering for textured surfaces at an oblique angle from the viewer.
* @memberof Context.prototype
* @type {Boolean}
* @see {@link https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/}
*/
textureFilterAnisotropic : {
get : function() {
return !!this._textureFilterAnisotropic;
Expand Down
15 changes: 9 additions & 6 deletions Source/Renderer/CubeMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ define([
}
gl.bindTexture(textureTarget, null);

this._gl = gl;
this._context = context;
this._textureFilterAnisotropic = context._textureFilterAnisotropic;
this._textureTarget = textureTarget;
this._texture = texture;
Expand Down Expand Up @@ -231,13 +231,16 @@ define([
(minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST) ||
(minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);

// float textures only support nearest filtering, so override the sampler's settings
if (this._pixelDatatype === PixelDatatype.FLOAT || this._pixelDatatype === PixelDatatype.HALF_FLOAT) {
var context = this._context;
var pixelDatatype = this._pixelDatatype;

// float textures only support nearest filtering unless the linear extensions are supported, so override the sampler's settings
if ((pixelDatatype === PixelDatatype.FLOAT && !context.textureFloatLinear) || (pixelDatatype === PixelDatatype.HALF_FLOAT && !context.textureHalfFloatLinear)) {
minificationFilter = mipmap ? TextureMinificationFilter.NEAREST_MIPMAP_NEAREST : TextureMinificationFilter.NEAREST;
magnificationFilter = TextureMagnificationFilter.NEAREST;
}

var gl = this._gl;
var gl = context._gl;
var target = this._textureTarget;

gl.activeTexture(gl.TEXTURE0);
Expand Down Expand Up @@ -332,7 +335,7 @@ define([

this._hasMipmap = true;

var gl = this._gl;
var gl = this._context._gl;
var target = this._textureTarget;
gl.hint(gl.GENERATE_MIPMAP_HINT, hint);
gl.activeTexture(gl.TEXTURE0);
Expand All @@ -346,7 +349,7 @@ define([
};

CubeMap.prototype.destroy = function() {
this._gl.deleteTexture(this._texture);
this._context._gl.deleteTexture(this._texture);
this._positiveX = destroyObject(this._positiveX);
this._negativeX = destroyObject(this._negativeX);
this._positiveY = destroyObject(this._positiveY);
Expand Down
9 changes: 6 additions & 3 deletions Source/Renderer/Texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,13 +385,16 @@ define([
(minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST) ||
(minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);

// float textures only support nearest filtering, so override the sampler's settings
if (this._pixelDatatype === PixelDatatype.FLOAT || this._pixelDatatype === PixelDatatype.HALF_FLOAT) {
var context = this._context;
var pixelDatatype = this._pixelDatatype;

// float textures only support nearest filtering unless the linear extensions are supported, so override the sampler's settings
if ((pixelDatatype === PixelDatatype.FLOAT && !context.textureFloatLinear) || (pixelDatatype === PixelDatatype.HALF_FLOAT && !context.textureHalfFloatLinear)) {
minificationFilter = mipmap ? TextureMinificationFilter.NEAREST_MIPMAP_NEAREST : TextureMinificationFilter.NEAREST;
magnificationFilter = TextureMagnificationFilter.NEAREST;
}

var gl = this._context._gl;
var gl = context._gl;
var target = this._textureTarget;

gl.activeTexture(gl.TEXTURE0);
Expand Down
173 changes: 173 additions & 0 deletions Specs/Renderer/CubeMapSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,91 @@ defineSuite([
});
});

it('creates a cube map with floating-point textures and linear filtering', function() {
if (!context.floatingPointTexture) {
return;
}

var positiveXColor = new Color(0.0, 1.0, 1.0, 1.0);
var negativeXColor = new Color(0.0, 0.0, 1.0, 1.0);
var positiveYColor = new Color(0.0, 1.0, 0.0, 1.0);
var negativeYColor = new Color(1.0, 0.0, 0.0, 1.0);
var positiveZColor = new Color(1.0, 0.0, 1.0, 1.0);
var negativeZColor = new Color(1.0, 1.0, 0.0, 1.0);

cubeMap = new CubeMap({
context : context,
source : {
positiveX : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([positiveXColor.red, positiveXColor.green, positiveXColor.blue, positiveXColor.alpha])
},
negativeX : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([negativeXColor.red, negativeXColor.green, negativeXColor.blue, negativeXColor.alpha])
},
positiveY : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([positiveYColor.red, positiveYColor.green, positiveYColor.blue, positiveYColor.alpha])
},
negativeY : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([negativeYColor.red, negativeYColor.green, negativeYColor.blue, negativeYColor.alpha])
},
positiveZ : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([positiveZColor.red, positiveZColor.green, positiveZColor.blue, positiveZColor.alpha])
},
negativeZ : {
width : 1,
height : 1,
arrayBufferView : new Float32Array([negativeZColor.red, negativeZColor.green, negativeZColor.blue, negativeZColor.alpha])
}
},
pixelDatatype : PixelDatatype.FLOAT,
sampler : new Sampler({
wrapS : TextureWrap.CLAMP_TO_EDGE,
wrapT : TextureWrap.CLAMP_TO_EDGE,
minificationFilter : TextureMinificationFilter.LINEAR,
magnificationFilter : TextureMagnificationFilter.LINEAR
})
});

var fs =
'uniform samplerCube u_texture;' +
'void main() { gl_FragColor = textureCube(u_texture, normalize(vec3(1.0, 1.0, 0.0))); }';

var uniformMap = {
u_texture : function() {
return cubeMap;
}
};

if (!context.textureFloatLinear) {
expect({
context : context,
fragmentShader : fs,
uniformMap : uniformMap,
epsilon : 1
}).contextToRender(positiveYColor.toBytes());
} else {
Color.multiplyByScalar(positiveXColor, 1.0 - 0.5, positiveXColor);
Color.multiplyByScalar(positiveYColor, 0.5, positiveYColor);
var color = Color.add(positiveXColor, positiveYColor, positiveXColor);
expect({
context : context,
fragmentShader : fs,
uniformMap : uniformMap,
epsilon : 1
}).contextToRender(color.toBytes());
}
});

it('creates a cube map with half floating-point textures', function() {
if (!context.halfFloatingPointTexture) {
return;
Expand Down Expand Up @@ -453,6 +538,94 @@ defineSuite([
});
});

it('creates a cube map with half floating-point textures and linear filtering', function() {
if (!context.halfFloatingPointTexture) {
return;
}

var positiveXFloats = [12902, 13926, 14541, 15360];
var negativeXFloats = [13926, 12902, 14541, 15360];
var positiveYFloats = [14541, 13926, 12902, 15360];
var negativeYFloats = [12902, 14541, 13926, 15360];
var positiveZFloats = [13926, 14541, 12902, 15360];
var negativeZFloats = [14541, 12902, 13926, 15360];

var positiveXColor = new Color(0.2, 0.4, 0.6, 1.0);
var positiveYColor = new Color(0.6, 0.4, 0.2, 1.0);

cubeMap = new CubeMap({
context : context,
source : {
positiveX : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(positiveXFloats)
},
negativeX : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(negativeXFloats)
},
positiveY : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(positiveYFloats)
},
negativeY : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(negativeYFloats)
},
positiveZ : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(positiveZFloats)
},
negativeZ : {
width : 1,
height : 1,
arrayBufferView : new Uint16Array(negativeZFloats)
}
},
pixelDatatype : PixelDatatype.HALF_FLOAT,
sampler : new Sampler({
wrapS : TextureWrap.CLAMP_TO_EDGE,
wrapT : TextureWrap.CLAMP_TO_EDGE,
minificationFilter : TextureMinificationFilter.LINEAR,
magnificationFilter : TextureMagnificationFilter.LINEAR
})
});

var fs =
'uniform samplerCube u_texture;' +
'void main() { gl_FragColor = textureCube(u_texture, normalize(vec3(1.0, 1.0, 0.0))); }';

var uniformMap = {
u_texture : function() {
return cubeMap;
}
};

if (!context.textureHalfFloatLinear) {
expect({
context : context,
fragmentShader : fs,
uniformMap : uniformMap,
epsilon : 1
}).contextToRender(positiveYColor.toBytes());
} else {
Color.multiplyByScalar(positiveXColor, 1.0 - 0.5, positiveXColor);
Color.multiplyByScalar(positiveYColor, 0.5, positiveYColor);
var color = Color.add(positiveXColor, positiveYColor, positiveXColor);
expect({
context : context,
fragmentShader : fs,
uniformMap : uniformMap,
epsilon : 1
}).contextToRender(color.toBytes());
}
});

it('creates a cube map with typed arrays and images', function() {
cubeMap = new CubeMap({
context : context,
Expand Down
Loading