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

Вход на сайт

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

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

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

Всем у кого не работает. файл wizard.script Ещё одно упоминание Glut32 в строке "if (!VerifyLibFile(dir_nomacro_lib, _T("glut32"), _T("GLUT's"))) return false;" меняем на "if (!VerifyLibFile(dir_nomacro_lib, _T("freeglut"), _T("GLUT's"))) return...
Не получается, емаё
огромное спасибо за подробное объяснение про 3д графику на питоне, в интернете очень мало подобной информации
dobryj den, popytalas otkryt prikreplionnyj fail ctoby posmotret kak rabotaet, no mne ego ne pokazyvaet vydajet osibku. Pochemu?
Очень интересно! ии сайт крутой жалко что умирает(

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

Рейтинг@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);	//передаем цвет
		}

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