Студопедия Главная Случайная страница Обратная связь

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

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; просмотров: 933. Нарушение авторских прав; Мы поможем в написании вашей работы!




Вычисление основной дактилоскопической формулы Вычислением основной дактоформулы обычно занимается следователь. Для этого все десять пальцев разбиваются на пять пар...


Расчетные и графические задания Равновесный объем - это объем, определяемый равенством спроса и предложения...


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


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

Влияние первой русской революции 1905-1907 гг. на Казахстан. Революция в России (1905-1907 гг.), дала первый толчок политическому пробуждению трудящихся Казахстана, развитию национально-освободительного рабочего движения против гнета. В Казахстане, находившемся далеко от политических центров Российской империи...

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

КОНСТРУКЦИЯ КОЛЕСНОЙ ПАРЫ ВАГОНА Тип колёсной пары определяется типом оси и диаметром колес. Согласно ГОСТ 4835-2006* устанавливаются типы колесных пар для грузовых вагонов с осями РУ1Ш и РВ2Ш и колесами диаметром по кругу катания 957 мм. Номинальный диаметр колеса – 950 мм...

Принципы, критерии и методы оценки и аттестации персонала   Аттестация персонала является одной их важнейших функций управления персоналом...

Пункты решения командира взвода на организацию боя. уяснение полученной задачи; оценка обстановки; принятие решения; проведение рекогносцировки; отдача боевого приказа; организация взаимодействия...

Что такое пропорции? Это соотношение частей целого между собой. Что может являться частями в образе или в луке...

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