具有环境光强度的Three.js片段着色器



我有一个Three.js着色器材质,它对视频纹理进行着色,使绿色变为透明。那部分很好用。

现在我正试图修改它,使其受到环境光强度的影响,基本上我想要的是,当环境光强度较低时,视频播放会变暗。

在图像上,我只需添加一个"标准材质"就可以做到这一点,所以我尝试在视频中添加两个单独的材质(chromakey着色器材质和一个标准材质(,但这无济于事。

所以我开始做一些研究并深入研究chromakey着色器的代码(这不是我写的(,我做了以下更改:

  • 我已经把原来的制服和THREE.UniformsLib["lights"]的制服合并了
  • 我已经在着色器材质的参数中启用了灯光

现在的问题是,如何访问片段着色器内部的环境光强度值(顺便说一下,该值不断更新(,以及如何根据强度值(介于0和1之间(使像素变暗。

着色器材质

var uniforms = THREE.UniformsUtils.clone(THREE.UniformsLib["lights"]);
uniforms['color'] = { type: 'c', value: data.color };
uniforms['texture'] = { type: 't', value: videoTexture };
this.material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: this.vertexShader,
fragmentShader: this.fragmentShader,
lights: true
});

顶点着色器

varying vec2 vUv;
void main(void)
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}

片段着色器

uniform sampler2D texture;
uniform vec3 color;
varying vec2 vUv;
void main(void)
{
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor, a);
}

我基本上需要根据光的强度修改tColor,但正如我所说,我不知道如何访问该值,以及如何根据该值使颜色变暗/变亮。

为碎片着色器添加亮度控制

如果它只是一个简单的亮度控制,你可以用标量乘以frag颜色。

示例碎片着色器

uniform sampler2D texture;
uniform vec3 color;
uniform float brightness;   // added uniform to control brightness
varying vec2 vUv;
void main(void) {
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor * brightness, a);  // scale brightness of rgb channels
}

然后在Javascript代码中支持新的统一

const BRIGHTNESS_MAX = 1;    // default value. MAX brightness DO not change this
// DO NOT CHANGE this value to set upper limit
// Use the const LUX_MAX (see below) to set upper limit
const BRIGHTNESS_MIN = 0.7;  // The darkest you want A value of 0 is black
// add brightness The same uniforms object as you 
// got with uniforms = THREE.UniformsUtils.clone(THREE.UniformsLib["lights"]);
uniforms.brightness = {value: BRIGHTNESS_MAX};  // default to max

更改统一的值

uniforms.brightness.value = brightness; // set the new value

来自三个文档

"所有统一值都可以自由更改(例如颜色、纹理、不透明度等(,每帧都会将值发送到着色器。">

所以这就是添加亮度控制所需的全部内容。

使用环境传感器

我假设您可以访问传感器值。传感器将亮度保持为绝对值LUX10为暗~707为正常,10000以上为亮。

您必须通过更改与BRIGHTNESS_MAX对应的LUX并将BRIGHTNESS_MIN设置为您希望图像变为最暗的值来校准传感器读数。

由于光传感器和显示设备的缩放比例和动态范围非常不同,以下函数假设渲染图像上的MAX_LUX和白色是相同的亮度

以下功能将从LUX值转换为亮度值

const MAX_LUX = 5000;  // This LUX value and above will set brightness to max
function LUX2Brightness(lux) {
if (lux >= MAX_LUX) { return BRIGHTNESS_MAX }
const MIN = (BRIGHTNESS_MIN ** 2.2) * MAX_LUX; // do not manually set this value
// Set BRIGHTNESS_MIN to control
// low light level
if (lux <= MIN) { return BRIGHTNESS_MIN }    
return  (lux ** (1 / 2.2)) / (MAX_LUX ** (1 / 2.2));
}

将上述函数与着色器一起使用

// luminosity is value from ambient light sensor event
uniforms.brightness.value = LUX2Brightness(luminosity);

假设您将MAX_LUX设置为全白色渲染图像的实际LUX输出(祝您好运(。

重要

这不是绝对的水平解决方案。

人类的视觉是适应性的。校准最小值和最大值的方式会发生变化,具体取决于您的眼睛如何适应当前的光照水平、显示渲染内容的设备上的当前亮度、颜色(以及更多(设置、相机的当前设置、曝光、白平衡(等等(以及您的个人艺术家偏好。

所有这些东西通常都是自动设置的,所以任何现在看起来不错的设置都可能不是早上或咖啡休息回来时想要的。

所有代码

片段着色器

uniform sampler2D texture;
uniform vec3 color;
uniform float brightness;   
varying vec2 vUv;
void main(void) {
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor * brightness, a); 
}

JavaScript设置代码

const BRIGHTNESS_MAX = 1; // Don't change this value
const BRIGHTNESS_MIN = 0.7;  
const MAX_LUX = 2000;  
uniforms.brightness = {value: BRIGHTNESS_MAX};
function LUX2Brightness(lux) {
if (lux >= MAX_LUX) { return BRIGHTNESS_MAX }
const MIN = (BRIGHTNESS_MIN ** 2.2) * MAX_LUX;     
if (lux <= MIN) { return BRIGHTNESS_MIN }    
return  (lux ** (1 / 2.2)) / (MAX_LUX ** (1 / 2.2));
}

传感器读数

将以下线路置于传感器事件中。例如"devicelight"事件侦听器。

uniforms.brightness.value = LUX2Brightness(event.value);

最新更新