Студопедия — OpenGL ES 2.0. Урок 5. Шейдер преломления света
Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

OpenGL ES 2.0. Урок 5. Шейдер преломления света






В предыдущем уроке мы рисовали гладкую поверхности в виде бегущей волны. В этом уроке мы попробуем превратить нашу поверхность в настоящую воду. Луч света проходящий, через границу двух сред, немного изменяет свое направление. Этот эффект называют преломлением света. Пусть на глубине y = ybottom расположено дно, покрытое текстурой. Над поверхностью воды находится глаз наблюдателя, т.е. камера. Нужно выяснить, какую точку текстуры дна увидит наблюдатель. Для этого изменим направление хода луча света на обратное. Луч света будет выходить из глаза наблюдателя, преломляться на поверхности воды и попадать на дно. Точка пересечения преломленного луча и дна и будет видна наблюдателю.

 

Разобьем задачу на три этапа:
1. Найти входящий вектор IN.
2. Найти преломленный вектор OUT.
3. Найти точку пересечения преломленного вектора с дном.

Приступаем к решению первого этапа. Входящий вектор найти нетрудно, если известны координаты вершины и координаты камеры. В фрагментном шейдере это будет выглядеть так:

vec3 IN=v_vertex - u_camera;

где u_camera - координаты камеры в виде униформы, а v_vertex - это вершина на поверхности воды, на которую положил глаз наблюдатель.

 

Вектор IN не обязательно должен быть нормализован. В предыдущих уроках при расчете освещения мы уже использовали вектор:

vec3 lookvector = normalize(u_camera - v_vertex)

Поэтому если мы хотим скомбинировать преломление с освещением можно в качестве вектора IN взять lookvector с противоположным знаком (т.е. IN = - lookvector), чтобы не создавать лишних вычислений в шейдере.


Приступаем к решению второго этапа.

Если нам известен входящий вектор IN, вектор единичной нормали N и относительный показатель преломления на границе двух сред k находим преломленный вектор OUT по закону Снелла:

Здесь:

 

 

Создадим в фрагментном шейдере функцию myrefract, которая будет принимать на вход аргументы IN, NORMAL, k и возвращать преломленный вектор:
vec3 myrefract(vec3 IN, vec3 NORMAL, float k){
// скалярное произведение нормали и вектора входящего луча
float nv=dot(NORMAL,IN);
// квадрат длины входящего вектора
float v2 = dot(IN,IN);
// коэффициент перед вектором нормали
float knormal=(sqrt(((k*k-1.0)*v2)/(nv*nv)+1.0)-1.0)* nv;
// вектор исходящего луча
vec3 OUT = IN + (knormal * NORMAL);
return OUT;
}

Можно перенести эту функцию из фрагментного в вершинный шейдер, при этом скорость отрисовки кадра возрастет в несколько раз, но визуальное качество жидкости ухудшится. Второй этап выполнен.
Приступаем к решению третьего этапа.

Найдем точку пересечения преломленного вектора с плоскостью дна. Зададим уравнение плоскости дна, оно выглядит очень просто:

у = ybottom, где ybottom - глубина дна

Как задать в пространстве уравнение прямой? Нужно знать какую-нибудь точку через которую проходит прямая и вектор, приложенный к этой точке. Преломленный вектор проходит через вершину v_vetrex. Хорошо, одну точку прямой мы знаем. Прицепим к ней вектор OUT, который мы уже нашли, и получим параметрическое уравнение прямой линии преломленного луча света:

x = v_vertex.x +OUT.x * t
y = v_vertex.y + OUT.y * t
z = v_vertex.z +OUT.z *t

где t - параметр, т.е. любое число. Все бесконечное множество чисел t дают нам бесконечное количество точек прямой. Кто хочет узнать больше про уравнение прямой линии в параметрическом виде могут обратиться к википедии. Нам нужно найти такой параметр t, который соответствует пересечению вектора OUT с плоскостью дна. Соединим вместе уравнение плоскости дна и уравнение прямой, т.е. подставим в уравнение прямой ybottom вместо у:

ybottom = v_vertex.y + OUT.y * t

Теперь мы знаем параметр t=(ybottom - v_vertex.y)/OUT.y

Подставляем найденный t в выражения для x и z получаем получаем координаты точки пересечения вектора OUT с дном:

xbottom = v_vertex.x +OUT.x * (ybottom - v_vertex.y)/OUT.y

zbottom = v_vertex.z +OUT.z * (ybottom - v_vertex.y)/OUT.y

Значение xbottom станет нашей текстурной координатой s, а zbottom - текстурной координатой t. Чтобы текстура не выглядела мелкой, уменьшим текстурные координаты в два раза. Получим двумерный вектор координат текстуры:

vec2 texCoord = vec2(0.5*xbottom,0.5*zbottom);

Пришло время оформить фрагментный шейдер. В нем я соединил эффект преломления с освещением:
precision mediump float;
uniform vec3 u_camera;
uniform vec3 u_lightPosition;
uniform sampler2D u_texture0;
varying vec3 v_vertex;
varying vec3 v_normal;


//функция преломления света, она должна быть объявлена до функции main
vec3 myrefract(vec3 IN, vec3 NORMAL, float k){
// скалярное произведение нормали и вектора входящего луча
float nv=dot(NORMAL,IN);
// квадрат длины входящего вектора
float v2 = dot(IN,IN);
//коэффициент перед вектором нормали
float knormal=(sqrt(((k*k-1.0)*v2)/(nv*nv)+1.0)-1.0)* nv;
// вектор исходящего луча
vec3 OUT = IN + (knormal * NORMAL);
return OUT;
}

void main() {
vec3 n_normal=normalize(v_normal);
vec3 lightvector = normalize(u_lightPosition - v_vertex);
vec3 lookvector = normalize(u_camera - v_vertex);
float ambient=0.1;
float k_diffuse=0.7;
float k_specular=0.3;
float diffuse = k_diffuse * max(dot(n_normal, lightvector), 0.0);
vec3 reflectvector = reflect(-lightvector, n_normal);
float specular = k_specular * pow(max(dot(lookvector,reflectvector),0.0), 40.0);
vec4 one=vec4(1.0,1.0,1.0,1.0);
//цвет освещения
vec4 lightColor=(ambient+diffuse+specular)*one;
//входящий вектор IN это (-lookvector)
//преломленный вектор OUT с показателем преломления 1.2
vec3 OUT=myrefract(-lookvector, n_normal, 1.2);
//глубина дна
float ybottom=-1.0;
//точка пересечения преломленного вектора и плоскости дна
float xbottom=v_vertex.x+OUT.x*(ybottom-v_vertex.y)/OUT.y;
float zbottom=v_vertex.z+OUT.z*(ybottom-v_vertex.y)/OUT.y;
//координаты текстуры
vec2 texCoord = vec2(0.5*xbottom,0.5*zbottom);
//цвет текстуры, извлеченный из текстуры по координатам
vec4 textureColor=texture2D(u_texture0, texCoord);
//цвет пикселя - комбинация цвета освещения и цвета текстуры
gl_FragColor=lightColor*textureColor;
}

Внесем в код предыдущего урока небольшие изменения. Заменим фрагментный шейдер, а также в методе onSurfaceCreated загрузим какую-нибудь текстуру, например такую:

 

//создадим текстурный объект
mTexture0=new Texture(context,R.drawable.picture);
//создадим шейдерный объект
mShader=new Shader(vertexShaderCode, fragmentShaderCode);
//свяжем буфер вершин с атрибутом a_vertex в вершинном шейдере
mShader.linkVertexBuffer(vertexBuffer);
//свяжем буфер нормалей с атрибутом a_normal в вершинном шейдере
mShader.linkNormalBuffer(normalBuffer);
//свяжем текстурный объект с шейдерным объектом
mShader.linkTexture(mTexture0, null);

Запусти приложение на исполнение и получим живую воду:

 







Дата добавления: 2015-10-19; просмотров: 899. Нарушение авторских прав; Мы поможем в написании вашей работы!



Кардиналистский и ординалистский подходы Кардиналистский (количественный подход) к анализу полезности основан на представлении о возможности измерения различных благ в условных единицах полезности...

Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...

Композиция из абстрактных геометрических фигур Данная композиция состоит из линий, штриховки, абстрактных геометрических форм...

Важнейшие способы обработки и анализа рядов динамики Не во всех случаях эмпирические данные рядов динамики позволяют определить тенденцию изменения явления во времени...

Вопрос. Отличие деятельности человека от поведения животных главные отличия деятельности человека от активности животных сводятся к следующему: 1...

Расчет концентрации титрованных растворов с помощью поправочного коэффициента При выполнении серийных анализов ГОСТ или ведомственная инструкция обычно предусматривают применение раствора заданной концентрации или заданного титра...

Психолого-педагогическая характеристика студенческой группы   Характеристика группы составляется по 407 группе очного отделения зооинженерного факультета, бакалавриата по направлению «Биология» РГАУ-МСХА имени К...

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

Случайной величины Плотностью распределения вероятностей непрерывной случайной величины Х называют функцию f(x) – первую производную от функции распределения F(x): Понятие плотность распределения вероятностей случайной величины Х для дискретной величины неприменима...

Схема рефлекторной дуги условного слюноотделительного рефлекса При неоднократном сочетании действия предупреждающего сигнала и безусловного пищевого раздражителя формируются...

Studopedia.info - Студопедия - 2014-2024 год . (0.01 сек.) русская версия | украинская версия