Три. js фрагментный шейдер с интенсивностью окружающего света - PullRequest
0 голосов
/ 27 марта 2020

У меня есть шейдерный материал Three. js, который обрабатывает текстуру видео, так что зеленый становится прозрачным. Эта часть работает нормально.

Теперь я пытаюсь изменить ее так, чтобы на нее влияла интенсивность окружающего освещения, в основном я хочу, чтобы, когда интенсивность окружающего света была ниже, воспроизведение видео становилось темнее .

На изображениях я могу это сделать, просто добавив стандартный материал, поэтому я попытался добавить к видео два отдельных материала (материал шейдера кеинга и стандартный), но это не помогло.

Итак, я начал проводить некоторые исследования и копаться в коде шейдера кеинга (который я не написал), и я сделал следующие изменения:

  • Я слил оригинал Униформа с теми из 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 в соответствии с интенсивностью света, но, как я уже сказал, я понятия не имею, как получить доступ к этому значению и как затемнить / осветлить цвет в соответствии с ним .

1 Ответ

1 голос
/ 28 марта 2020

Добавление управления яркостью к фрагментному шейдеру

Если это просто простой контроль яркости, вы можете умножить цвет фрагмента на скаляр.

Пример фрагмента шейдера

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

С Три документа

"Все значения униформ можно свободно изменять (например, цвета, текстуры, прозрачность и т. Д. c), значения отправляются в шейдер каждый кадр."

Так что это все, что нужно для добавления регулировки яркости.

Использование датчика окружающей среды

Я предполагаю, что у вас есть доступ к значению датчика. Датчик удерживает уровень освещенности как абсолютное значение LUX 10 темно ~ 707 нормально и 10000 плюс ярко.

Вам нужно будет откалибровать показания датчика, изменив значение LUX, соответствующее BRIGHTNESS_MAX и установив BRIGHTNESS_MIN на самое темное, каким вы хотите, чтобы изображение стало.

Поскольку диапазон масштабирования и динамический диапазон c для датчика освещенности и устройства отображения сильно различаются, следующая функция предполагает, что 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);
...