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

WebGLRenderer: Support "Linear Display P3" working color space and "Display P3" unlit rendering #26644

Merged
merged 21 commits into from
Sep 7, 2023
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
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
"webgl_sprites",
"webgl_test_memory",
"webgl_test_memory2",
"webgl_test_wide_gamut",
"webgl_tonemapping",
"webgl_video_kinect",
"webgl_video_panorama_equirectangular",
Expand Down
17 changes: 17 additions & 0 deletions examples/jsm/capabilities/WebGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ class WebGL {

}

static isColorSpaceAvailable( colorSpace ) {

try {

const canvas = document.createElement( 'canvas' );
const ctx = window.WebGL2RenderingContext && canvas.getContext( 'webgl2' );
ctx.drawingBufferColorSpace = colorSpace;
return ctx.drawingBufferColorSpace === colorSpace; // deepscan-disable-line SAME_OPERAND_VALUE

} catch ( e ) {

return false;

}

}

static getWebGLErrorMessage() {

return this.getErrorMessage( 1 );
Expand Down
5 changes: 3 additions & 2 deletions examples/jsm/postprocessing/OutputPass.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
ColorManagement,
RawShaderMaterial,
UniformsUtils,
LinearToneMapping,
ReinhardToneMapping,
CineonToneMapping,
ACESFilmicToneMapping,
SRGBColorSpace
SRGBTransfer
} from 'three';
import { Pass, FullScreenQuad } from './Pass.js';
import { OutputShader } from '../shaders/OutputShader.js';
Expand Down Expand Up @@ -51,7 +52,7 @@ class OutputPass extends Pass {

this.material.defines = {};

if ( this._outputColorSpace == SRGBColorSpace ) this.material.defines.SRGB_COLOR_SPACE = '';
if ( ColorManagement.getTransfer( this._outputColorSpace ) === SRGBTransfer ) this.material.defines.SRGB_TRANSFER = '';

if ( this._toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = '';
else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/shaders/GammaCorrectionShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const GammaCorrectionShader = {

vec4 tex = texture2D( tDiffuse, vUv );

gl_FragColor = LinearTosRGB( tex );
gl_FragColor = sRGBTransferOETF( tex );

}`

Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/shaders/OutputShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ const OutputShader = {

// color space

#ifdef SRGB_COLOR_SPACE
#ifdef SRGB_TRANSFER

gl_FragColor = LinearTosRGB( gl_FragColor );
gl_FragColor = sRGBTransferOETF( gl_FragColor );

#endif

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/textures/wide_gamut/logo_p3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/textures/wide_gamut/logo_srgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
232 changes: 232 additions & 0 deletions examples/webgl_test_wide_gamut.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - test - wide gamut</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
<style>
.container {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
}

.slider {
position: absolute;
cursor: ew-resize;

width: 40px;
height: 40px;
background-color: #F32196;
opacity: 0.7;
border-radius: 50%;

top: calc(50% - 20px);
left: calc(50% - 20px);
}

.label {
position: fixed;
top: calc(50% - 1em);
height: 2em;
line-height: 2em;
background: rgba(0, 0, 0, 0.5);
margin: 0;
padding: 0.2em 0.5em;
border-radius: 4px;
font-size: 14px;
user-select: none;
-webkit-user-select: none;
}
</style>
</head>

<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - wide gamut test<br />
</div>

<div class="container">
<div class="slider"></div>
<p class="label" style="left: 1em;">sRGB</p>
<p class="label" style="right: 1em;">Display P3</p>
</div>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';

import WebGL from 'three/addons/capabilities/WebGL.js';
Fixed Show fixed Hide fixed

let container, camera, renderer, loader;
let sceneL, sceneR, textureL, textureR;

let sliderPos = window.innerWidth / 2;

const isP3Context = WebGL.isColorSpaceAvailable( THREE.DisplayP3ColorSpace );

if ( isP3Context ) {

THREE.ColorManagement.workingColorSpace = THREE.LinearDisplayP3ColorSpace;

}

init();

function init() {

container = document.querySelector( '.container' );

sceneL = new THREE.Scene();
sceneR = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.z = 6;

loader = new THREE.TextureLoader();

initTextures();
initSlider();

renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setScissorTest( true );
renderer.setAnimationLoop( render );
container.appendChild( renderer.domElement );

if ( isP3Context && window.matchMedia( '( color-gamut: p3 )' ).matches ) {

renderer.outputColorSpace = THREE.DisplayP3ColorSpace;

}

window.addEventListener( 'resize', onWindowResize );
window.matchMedia( '( color-gamut: p3 )' ).addEventListener( 'change', onGamutChange );

}

async function initTextures() {

const path = 'textures/wide_gamut/logo_{colorSpace}.png';

textureL = await loader.loadAsync( path.replace( '{colorSpace}', 'srgb' ) );
textureR = await loader.loadAsync( path.replace( '{colorSpace}', 'p3' ) );

textureL.colorSpace = THREE.SRGBColorSpace;
textureR.colorSpace = THREE.DisplayP3ColorSpace;

sceneL.background = containTexture( window.innerWidth / window.innerHeight, textureL );
sceneR.background = containTexture( window.innerWidth / window.innerHeight, textureR );

}

function initSlider() {

const slider = document.querySelector( '.slider' );

function onPointerDown() {

if ( event.isPrimary === false ) return;

window.addEventListener( 'pointermove', onPointerMove );
window.addEventListener( 'pointerup', onPointerUp );

}

function onPointerUp() {

window.removeEventListener( 'pointermove', onPointerMove );
window.removeEventListener( 'pointerup', onPointerUp );

}

function onPointerMove( e ) {

if ( event.isPrimary === false ) return;

sliderPos = Math.max( 0, Math.min( window.innerWidth, e.pageX ) );

slider.style.left = sliderPos - ( slider.offsetWidth / 2 ) + 'px';

}

slider.style.touchAction = 'none'; // disable touch scroll
slider.addEventListener( 'pointerdown', onPointerDown );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

containTexture( window.innerWidth / window.innerHeight, sceneL.background );
containTexture( window.innerWidth / window.innerHeight, sceneR.background );

}

function onGamutChange( { matches } ) {

renderer.outputColorSpace = isP3Context && matches ? THREE.DisplayP3ColorSpace : THREE.SRGBColorSpace;

textureL.needsUpdate = true;
textureR.needsUpdate = true;

}

function containTexture ( aspect, target ) {

// Sets the matrix uv transform so the texture image is contained in a region having the specified aspect ratio,
// and does so without distortion. Akin to CSS object-fit: contain.
// Source: https://github.com/mrdoob/three.js/pull/17199

var imageAspect = ( target.image && target.image.width ) ? target.image.width / target.image.height : 1;

if ( aspect > imageAspect ) {

target.matrix.setUvTransform( 0, 0, aspect / imageAspect, 1, 0, 0.5, 0.5 );

} else {

target.matrix.setUvTransform( 0, 0, 1, imageAspect / aspect, 0, 0.5, 0.5 );

}

target.matrixAutoUpdate = false;

return target;

}

function render() {

renderer.setScissor( 0, 0, sliderPos, window.innerHeight );
renderer.render( sceneL, camera );

renderer.setScissor( sliderPos, 0, window.innerWidth, window.innerHeight );
renderer.render( sceneR, camera );

}

</script>
</body>
</html>
6 changes: 6 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ export const LinearSRGBColorSpace = 'srgb-linear';
export const DisplayP3ColorSpace = 'display-p3';
export const LinearDisplayP3ColorSpace = 'display-p3-linear';

export const LinearTransfer = 'linear';
export const SRGBTransfer = 'srgb';

export const Rec709Primaries = 'rec709';
export const P3Primaries = 'p3';

export const ZeroStencilOp = 0;
export const KeepStencilOp = 7680;
export const ReplaceStencilOp = 7681;
Expand Down
Loading