Уроки, алгоритмы, программы, примеры

Вход на сайт

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

Выдаёт ошибку glut32.dll не найден! При том, что он лежит в System32! Всё решил) Нужно отправить не в System32, а в System.
Спасибо за статью. Я не Ваш студент. Но мне она помогла написать функцию для Канторова множества на Python для черепашки: import turtle def kanter(x, y, d):     if d > 1:         turtle...
Как реализовать в данном примере границы расчёта?

Счетчики и рейтинг

Рейтинг@Mail.ru Яндекс.Метрика


Шум Перлина

Perlin noise (Шум Перлина, также иногда Классический шум Перлина) — математический алгоритм по генерированию процедурной текстуры псевдо-случайным методом. Используется в компьютерной графике для увеличения реализма или графической сложности поверхности геометрических объектов.
Шум Перлина — это градиентный шум, состоящий из набора псевдослучайных единичных векторов (направлений градиента), расположенных в определенных точках пространства и интерполированных функцией сглаживания между этими точками. Для генерации шума Перлина в одномерном пространстве необходимо для каждой точки этого пространства вычислить значение шумовой функции, используя направление градиента (или наклон) в указанной точке.

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

Данная статья приводит реализацию шума Перлина на языке шейдеров GLSL с использованием WebGL.

Для работы с шейдерами в HTML/js нам необходимо создать файл index.html.

Пишем стандартную структуру и добавляем элемент canvas id="canvas":

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title> Шумы </title>
    </head>
    <body>
        <main>
            <canvas id="canvas" width="500" height="500"></canvas>        
        </main>

Теперь создаем два скрипта типа shader. Первый - вершинный, id="Vshader". Второй - фрагментный, id="Fshader".

        <!-- VERTEX SHADER -->
        <script type="shader" id="Vshader">
 
        </script>
 
 
        <!-- FRAGMENT SHADER -->
        <script type="shader" id="Fshader"> 
 
        </script>

JS-скрипт:

        <script>
 
        </script>
    </body>
</html>

JavaScript

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

function startWebGL() {

получаем исходники шейдеров и webgl контекст:
            //получаем исходники шейдеров
        var vertexShaderText = document.getElementById('Vshader').text;
        var fragmentShaderText = document.getElementById('Fshader').text;
                        //получаем webgl context
        var canvas = document.getElementById('canvas');
        var gl = canvas.getContext('webgl');    
 
 

Теперь создадим наши шейдеры в контексте, передадим ему исходный код шейдеров, скомпилируем их, создадим программу и подключим к ней шейдеры:
 
        var vertexShader = gl.createShader(gl.VERTEX_SHADER);	//создаем шейдеры
        var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
 
 
        gl.shaderSource(vertexShader, vertexShaderText);		//передаем исходники
        gl.shaderSource(fragmentShader, fragmentShaderText);
 
        //компилируем. В случае неудачной компиляции  выводим ошибку
        gl.compileShader(vertexShader);
        if(!gl.getShaderParameter(vertexShader,gl.COMPILE_STATUS)){
            alert('ERROR COMPILING SHADER');
            console.error('SHADER ERR: ',gl.getShaderInfoLog(vertexShader));
        }
 
        gl.compileShader(fragmentShader);
        if(!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)){
            alert('ERROR COMPILING SHADER');
            console.error('SHADER ERR: ',gl.getShaderInfoLog(fragmentShader));
        }
 
 
        var program = gl.createProgram();		//создаем программу
        gl.attachShader(program, vertexShader);	//подключаем скомпилированные шейдеры
        gl.attachShader(program, fragmentShader);	
 
        gl.linkProgram(program);  //связываем
 

Немного о работе вершинных шейдеров. Дело в том, что внутри самой шейдерной программы создать массив данных, исходя из которых он будет строиться, мы не можем. Эта задача передается, в нашем случае, js. Для того, чтобы наладить связь между js и шейдерами, существуют atribut-, uniform- и varing-переменные. Сейчас мы создадим буффер вершин и передадим данные в атрибут-переменную:
 
        var vertexBuffer = gl.createBuffer();	//создаем буфер вершин
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); //связываем 
 
        var vertexArray = [		//массив вершин двух треугольников(из которых получена наша область работы)
 
            1.0, -1.0,
            -1.0,  1.0,
            -1.0, -1.0,
 
            -1.0,  1.0,
            1.0,  1.0,
            1.0, -1.0
        ];
 
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexArray), gl.STATIC_DRAW);//передаем данные нашего массива вершин в буфер вершин webgl
 
        //создаем атрибут, с помощью которого шейдер будет получать данные из буфера
        var positionAttribLocation = gl.getAttribLocation(program, 'vertexPosition');
 
 
        gl.vertexAttribPointer(
            positionAttribLocation,            //индекс для атрибута
            2,						//количество считываемых на одну вершину элементов
            gl.FLOAT,				//тип данных
            gl.FALSE,				//не нормализуем вершины
            0,						//нет шага
            0						// нет смещения
            );
 
        gl.enableVertexAttribArray(positionAttribLocation);//включаем наш атрибут
 

Теперь создадим uniform-переменную времени:
        var timeLocation = gl.getUniformLocation(program, "u_time");//создаем униформ переменную времени
 
 
			function renderLoop(timeStamp) {//рекурсивная функция отрисовки
 
 
			  gl.uniform1f(timeLocation, timeStamp/1000.0);	//передаем время в униформ переменную
 
			  gl.drawArrays(gl.TRIANGLES, 0, 6);	//отрисовываем два треугольника, дающих наш квадрат
 
 
			  window.requestAnimationFrame(renderLoop);	//вызываем функцию для зацикливания
			}
			gl.useProgram(program);      //выбираем используемую программу(в нашем случае вариантов не много)
			window.requestAnimationFrame(renderLoop);    // запускаем рекурсивную функцию, которая создаст нам анимацию и uniform переменную.
 
 
		}

С js почти все, осталось только запустить функцию:
		startWebGL();

Теперь переходим к скриптам с шейдерами.

С вершинным все просто:

attribute vec2 vertexPosition;		//атрибут вершин
 
		void main(){
			gl_Position = vec4(vertexPosition, 0.0, 1.0);//строим вершинный шейдер
 
		}

Фрагментный шейдер:
 
        precision mediump float; //устанавливаем точность для чисел с плавающей точкой
 
	uniform float u_time;	//униформ переменная времени

Итак, переходим к генерации шума. Первое, что нам здесь нужно - функция имитирующая случайность. Вообще, основой для данной функции служит y=fract(sin(x)*a). При больших значениях а получается некое подобие беспорядка, псевдо-случайность (можно посмотреть здесь www.iquilezles.org/apps/graphtoy/?f1(x)=fract(sin(x)*10000000) ). В нашем случае нужно было перенести этот эффект на плоскость, поэтому мы использовали скалярное произведение. Функция от скалярного произведения возвращает единственное число в диапазоне от 0.0 до 1.0 в зависимости от взаимного расположения векторов:
		float random (in vec2 st) { //функция генерации случайных чисел
			//dot - скалярное произведение
    		return fract(sin(dot(st.xy,vec2(12.9898,78.233)))* 43758.5453123);
		}

Шум. Шум Перлина

переходим непосредственно к шуму:

							//функция шума 
 
		float noise (in vec2 st) { //in - квалификатор(переданная переменная предназначена 
								   //только для чтения)

разделим наш входной вектор на целую и дробную части:
	    vec2 i = floor(st);		//целая часть st
	    vec2 f = fract(st);		//дробная часть st

теперь возьмем случайные значения для 4 углов нашей области, относительно которых будет проводится дальнейшая работа:
	    float a = random(i + vec2(0.0, 0.0));  //нижний левый угол
	    float b = random(i + vec2(1.0, 0.0));  //нижний правый угол
	    float c = random(i + vec2(0.0, 1.0));  //верхний левый угол
	    float d = random(i + vec2(1.0, 1.0));  //верхний правый угол

создадим вектор, который будет сглаживать наш шум:
	    //кубическая кривая
	    vec2 u=smoothstep(0.0,1.0, f);

И наконец, интерполируем:
	    return mix(a, b, u.x) +					//интерполируем между четырмя случайными значениями
	            (c - a)* u.y * (1.0 - u.x) +	
	            (d - b) * u.x * u.y;
		}

в основной функции мы нормализуем область отрисовки, масштабируем и создаем переменную, содержащую шум:

		void main(){
 
			vec2 st = gl_FragCoord.xy/vec2(500, 500);//нормализуем область, разделяя их на разрешение изображения
 
			vec2 pos = vec2(st*4.0);  //масштабируем
 
			float n = noise(pos);	 //накладываем шум
			//капельки
			n+=smoothstep(.15,.2,noise(st*20.));

осталось только передать наше шумовое значение переменной цвета.Чтобы значения цвета варьировались, создавая градиенты, я умножил цвет на uniform-переменную u_time, разделил ее на пять (чтобы замедлить анимацию) и все это взял в функцию fract(), чтобы значения не выходили за пределы [0.0; 1.0]. Я выбрал красный цвет, но можно присвоить и другим цветам, более того, можно создать несколько переменных и взять их в какие-нибудь функции:
			gl_FragColor = vec4(vec3(fract(n*u_time/5.),0.0,0.0), 1.0);	//передаем цвет
		}

На этом все. Благодарю за внимание.