X   Сообщение сайта
(Сообщение закроется через 3 секунды)



 

Здравствуйте, гость (

| Вход | Регистрация )

2 страниц V   1 2 >
Открыть тему
Тема закрыта
> Знакомство с WebGL, первые опыты
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:30; Ответить: Arks
Сообщение #1


Итак, для тех кто незнаком с этой чудесной технологией(вот как я например пару месяцев назад), и кому она интересна, предлагаю небольшой HelloWorld - на выходе получаем трехмерную вращающуюся пирамиду! (браузер firefox).
Официальна поддержка WebGL заявлена начиная с версий - opera 12, chrome 17, ff 4, safari 4

Данный пример будет полезен людям, имеющим опыт программирования на javascript которые теряются в спецификациях WebGL и не имеют опыта программирование на OpenGL ES2

структура:
- js
- WebGL
- core.js
- test
- objects
- pyramid.ibo
- pyramyd.vbo
- shaders
- base.fragment
- base.vertex
- program1.js
- gl-matrix-min.js
- loader.js
- index.php


[attachment=39321:test1.zip]

Дальше я немного подробнее расскажу о каждом из них.

Если Вам интересен пример и есть вопросы - смело задавайте их в ЛС или skype(напишите url темы или как-то обозначьте)
0
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:33; Ответить: Arks
Сообщение #2


index.php
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/js/gl-matrix-min.js"></script>
<script type="text/javascript" src="/js/loader.js"></script>
<script type="text/javascript">
try {
load('/js/WebGL/core.js', function(){
this.load('/js/test/program1.js', this.canvas({
width: 400,
height: 400
}));
}, 'WebGL');
} catch (e) {
alert(e);
}
</script>
</head>
<body>
</body>
</html>


Данный файл служит точкой входа в приложение. Он пробует подключить файл core.js и после его включения загружает и выполяет файл program1.js для свежесозданного canvas'а
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:34; Ответить: Arks
Сообщение #3


core.js
[JS]
(function(){
var glob = this, module = {
programs: {},
clone: function(obj) {
for(var i in this) {
obj[i] = this[i];
}
},
uid: function() {
guid = 0;
return function(){
return 'guid_'+(++guid);
};
}(),
canvas: function(settings){
var canvas, container = (settings.container && document.getElementById(settings.container)) || document.body;
canvas = document.createElement('canvas');
canvas.width = settings.width || 300;
canvas.height = settings.height || 150;
container.appendChild(canvas);
return canvas;
},
load: function(programPath, canvas) {
var core = this;
load(programPath, function(){
this.canvas = canvas;
core.init(this);
}, this.uid(), core.programs);
},
init: function(program) {
program.gl = this.getGL(program.canvas);
if(!program.gl) {
throw('browser don\'t supports WebGL');
}
this.loadShaders(program);
},
onShadersLoad: function(program) {
var prop;
program.program = program.gl.createProgram();
for(prop in program.shaders.vertex) {
program.gl.attachShader(program.program, program.shaders.vertex[prop].shader);
}
for(prop in program.shaders.fragment) {
program.gl.attachShader(program.program, program.shaders.fragment[prop].shader);
}
program.gl.linkProgram(program.program);
program.gl.useProgram(program.program);
this.getAttributesAddress(program);
this.getUniformsAddress(program);
this.loadBuffers(program);
},
getAttributesAddress: function(program) {
program.attributes = {};
for(i=0,len=program.settings.attributes.length;i<len;i++) {
program.attributes[program.settings.attributes[i]] = program.gl.getAttribLocation(program.program, program.settings.attributes[i]);
}
},
getUniformsAddress: function(program) {
program.uniforms = {};
for(i=0,len=program.settings.uniforms.length;i<len;i++) {
program.uniforms[program.settings.uniforms[i]] = program.gl.getUniformLocation(program.program, program.settings.uniforms[i]);
}
},
loadBuffers: function(program) {
var prop, buffers = program.settings.objects,
need = 0, total = 0, core = this;
for(prop in buffers.coordinates) {
need++;
}
for(prop in buffers.edges) {
need++;
}
for(prop in buffers.coordinates) {
load(buffers.coordinates[prop], function(responseText){
this.data = JSON.parse(responseText);
this.buffer = program.gl.createBuffer();
total++;
if(total == need) {
core.onObjectsLoad(program);
}
}, 'objects.vbo.'+prop, program, true);
}
for(prop in buffers.edges) {
load(buffers.edges[prop], function(responseText){
this.data = JSON.parse(responseText);
this.buffer = program.gl.createBuffer();
total++;
if(total == need) {
core.onObjectsLoad(program);
}
}, 'objects.ibo.'+prop, program, true);
}
},
onObjectsLoad: function(program) {
program.camera = {
position: [program.settings.camera.position.x,program.settings.camera.position.y,program.s
ettings.camera.position.z]
};
program.draw(this);
},
getGL: function(canvas) {
return canvas.getContext('experimental-webgl');
},
loadShaders: function(program) {
var prop, shaders = program.settings.shaders,
need = 0, total = 0, core = this;
for(prop in shaders.vertex) {
need++;
}
for(prop in shaders.fragment) {
need++;
}
for(prop in shaders.vertex) {
load(shaders.vertex[prop], function(shaderText){
this.raw = shaderText;
this.shader = program.gl.createShader(program.gl.VERTEX_SHADER);
program.gl.shaderSource(this.shader, this.raw);
program.gl.compileShader(this.shader);
total++;
if(total == need) {
core.onShadersLoad(program);
}
}, 'shaders.vertex.'+prop, program, true);
}
for(prop in shaders.fragment) {
load(shaders.fragment[prop], function(shaderText){
this.raw = shaderText;
this.shader = program.gl.createShader(program.gl.FRAGMENT_SHADER);
program.gl.shaderSource(this.shader, this.raw);
program.gl.compileShader(this.shader);
total++;
if(total == need) {
core.onShadersLoad(program);
}
}, 'shaders.fragment.'+prop, program, true);
}
},
run: function(program) {

}
};
return glob.onload = function(){
module.clone(this);
}
})();
[/JS]

Данный файл выполяет всю рутинную работу - он умеет создавать канвасы, и сокращает объем рутинных операций с инстансом webgl - контекста канвасов.
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:34; Ответить: Arks
Сообщение #4


loader.js
[JS]
(function(){
var glob = this, OK = true, scriptID = 0;
var map = function(ns, context) {
var parts = ns.split('.');
var cur = context || glob, next = parts.shift();
while(next) {
if(cur[next] == undefined) {
cur[next] = {};
}
cur = cur[next];
next = parts.shift();
}
return cur;
};
if(typeof(glob.XMLHttpRequest) !== 'function') {
OK = false;
}
glob.load = function(url, cb, ns, context, returnRaw) {
var xhr;
if(!OK) {
throw 'loader is not available';
}
xhr = new glob.XMLHttpRequest();
xhr.open('GET', url, true);
xhr.send(null);
xhr.onreadystatechange = function(){
var resource, script;
if(xhr.readyState == 4) {
if(xhr.status == 200 || (xhr.status == 0 && glob.document.domain.length == 0)) {
var obj;
if(returnRaw == undefined) {
resource = glob.document.createElement('script');
resource.id = 'src_'+(++scriptID).toString();
resource.innerHTML = xhr.responseText;
glob.document.head.appendChild(resource);
if(glob.onload != undefined) {
obj = map(ns, context);
glob.onload.call(obj || glob);
delete glob.module;
script = document.getElementById('src_'+scriptID);
script.parentNode.removeChild(script);
if(cb != undefined) cb.call(obj || glob);
return;
}
throw('modules should provide method onload() to init theirs functions');
} else {
obj = map(ns, context);
if(cb != undefined) cb.call((obj || glob), xhr.responseText);
return;
}
}
throw('requested url is not available and return error status: '+xhr.status);
}
}
}
return glob.load;
})();
[/JS]

данный файл просто загружает другие файлы, и умеет или включать их в контекст другого json-объекта или просто отдает полученное содержимое. В зависимости от того что требуется
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:36; Ответить: Arks
Сообщение #5


pyramyd.ibo
[JS]
[
0,1,4,
1,2,4,
2,3,4,
3,0,4
]
[/JS]

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


pyramid.vbo
[JS]
[
-0.5,-0.5,-0.5,
-0.5,-0.5,0.5,
0.5,-0.5,0.5,
0.5,-0.5,-0.5,
0.0,0.5,0.0
]
[/JS]

данный файл содержит координаты самих вершин в 3-мерном пространстве.
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:37; Ответить: Arks
Сообщение #6


base.fragment

[JS]
void main(void) {
gl_FragColor = vec4(0.2,0.2,0.2, 1.0);
}
[/JS]

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

base.vertex
[JS]
attribute vec3 aVertexPosition;
uniform mat4 perspective;
uniform mat4 move;
void main(void) {
gl_Position = perspective * move * vec4(aVertexPosition, 1.0);
gl_PointSize = 1.0;
}
[/JS]

данный файл содержит код вершинного шейдера, с помощью которого видеокарта преобразует исходные координаты объекта к координатам относительно наблюдателя.
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 22:54; Ответить: Arks
Сообщение #7


program1.js

[JS]
(function(){
var glob = this;
return glob.onload = function(){
this.settings = {
attributes: ['aVertexPosition'],
uniforms: ['perspective', 'move'],
shaders: {
vertex: {
base: '/js/test/shaders/base.vertex'
},
fragment: {
base: '/js/test/shaders/base.fragment'
}
},
objects: {
coordinates: {
pyramid: '/js/test/objects/pyramid.vbo'
},
edges: {
pyramid: '/js/test/objects/pyramid.ibo'
}
},
camera: {
position: {
x: 0.0,
y: 0.0,
z: -5.0
}
}
};
this.draw = function(core) {
var program = this;
var settings = {
yRotate: 0
};
setInterval(function(){
settings.yRotate++;
if(settings.yRotate == 90) settings.yRotate = 0;
program.run.call(program, settings);
}, 20);
program.run.call(program, settings);
}
this.run = function(settings){
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
this.gl.clearColor(0.9,0.9,0.9,1.0);
this.gl.clear(this.gl.COLOR_BUFFER_BIT);

var perspective = mat4.create();
var move = mat4.create();
mat4.perspective(20, this.canvas.width / this.canvas.height, 0.1, 10000.0, perspective);
mat4.identity(move);
mat4.translate(move, this.camera.position);
mat4.rotate(move, Math.PI/180*(settings.yRotate), [0, 1, 0]);

this.gl.uniformMatrix4fv(this.uniforms.perspective, false, perspective);
this.gl.uniformMatrix4fv(this.uniforms.move, false, move);

this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.objects.vbo.pyramid.buffer);
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.objects.vbo.pyramid.data), this.gl.STATIC_DRAW);
this.gl.vertexAttribPointer(this.attributes.aVertexPosition, 3, this.gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(this.attributes.aVertexPosition);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);

this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.objects.ibo.pyramid.buffer);
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.objects.ibo.pyramid.data), this.gl.STATIC_DRAW);
this.gl.drawElements(this.gl.TRIANGLES, this.objects.ibo.pyramid.data.length, this.gl.UNSIGNED_SHORT, 0);
}
}
})();
[/JS]

Самый главный файл WebGL-приложения, который фактически и содержит всю внутреннюю логику и отвечает за рисование
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 23:28; Ответить: Arks
Сообщение #8


Расскажу о терминологии WebGL и ее частей применительно к js(ES5):


  • типизированные массивы - встроенный объект js ES5
    тут все просто - имеем массив например Uint16Array в который передаем в конструктор обычный массив. Видеокарточка и OpenGL понимает только подобные строго-типизированные массивы.
  • шейдер
    является способом вывода картинки на экран. Шейдеры работают через физические конвейеры видеокарточки - конвейеров очень много так что шейдеры обрабатываются параллельно. Шейдеры пишутся на языке шейдеров OSL, который является по сути оберткой и вместе с тем урезанной версией C
    • пиксельный(или фрагментный) -
      например у нас в 3-мерном пространстве заданы 3 точки, которые образуют в 3-мерном пространстве треугольник. Чтобы нарисовать его на экране не просто как 3 точки, а как часть плоскости заключенной между ними видеокарточка рассчитывает цвет каждого физического пикселя на экране, сравнивая по сложному интегральному алгоритму удаленность пикселя от 3 точек и "взвешивая" их цвета. В моем примере пиксельный шейдер просто возвращает один цвет(серый) для каждого пикселя. В реальной жизни используются модели Фонга, Блинна, Орена-Нейера и прочие их разновидности. Лично я изучал геометрическую и прочую оптику в институте и поэтому меня от их упрощений тошнит.
    • вершинный
      например у нас в 3-мерном пространстве заданы 3 точки. Вершинный шейдер это просто функция, которая применяется к каждой из этих точек и позволяет преобразовать исходные координаты в 2-мерные координаты "на экране

  • буфер
    разновидность хеш-объекта который хранится в памяти видеокарты. Так уж сложилось что в процессе выполнения программы, она работает одновременно лишь с один выбранным буфером (gl.bindBuffer). Соответственно между буферами можно переключаться, менять их содержимое, рисовать их содержимое(передавать в так называемый frame-буффер) и т.д.
  • атрибут
    переменная в памяти видеокарты которая относится к шейдеру и позволяет передать в него данные "извне", т.е. из javascript
  • постоянная(uniform)
    переменная в памяти видеокарты, которая относится к программе(ко всем шейдерам) и позволяет передать в них данные "извне" - не может меняться в процессе обработки внутри шейдеров
  • переменная шейдеров(varying)
    переменная которая служит для передачи данных из вершинного шейдера в пиксельный(в моем примере оно не нужно и не используется)
  • матрицы, векторы
    типы данных языка шейдеров. фактически обычные массивы(2-х, 3-х и 4-х элементные), расширенные доп. плюшками из алгебры - типа быстрых алгоритмов перемножения. Для javascript в моем примере их возможности эмулируются сторонней библиотекой gl-matrix-min.js
    Часто бывает удобно, пусть и более медленно, один раз рассчитать матрицу на js чем почти мгновенно, но 100500 раз в каждом шейдере(помните, вершинный шейдер применяется к каждой вершине)
  • вершины
    не относятся как таковые к терминологии. Фактически явлются хеш-объектами вида { <index>: [<x>,<z>,<y>]}
    отсюда и взялось разделение на буферы координат вершин и буферы индексов вершин
  • программа
    некая муть содержащая скомпилированные в машинный код шейдеры, заполненные координатами/индексами буферы, а также константы(uniform) и атрибуты(attribute) для передачи в шейдеры
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 28.11.2012, 23:43; Ответить: Arks
Сообщение #9


Небольшая памятка по часто-используемым функциям:

gl.clearColor(float r, float g, float b, float a): void[/FONT]
установка gl.COLOR_CLEAR_VALUE, установка цвета для gl.clear
gl.getParameter(gl.COLOR_CLEAR_VALUE): Float32Array
получение цвета для очистки холста в удобочитаемом виде
gl.clear(gl.COLOR_BUFFER_BIT):void
очистка холста
gl.viewport(x,y,w,h)
установка поля зрения
gl.createBuffer():WebGLBuffer
создание буффера
gl.bindBuffer(gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER, WebGLBuffer buffer):void
переключение буффера в работу
gl.bindBuffer(gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER, null):void
отключение от текущего буффера
gl.bufferData(gl.ARRAY_BUFFER|gl.ELEMENT_ARRAY_BUFFER, data, <type>):void
установка данных в буффер; константы STATIC, DYNAMYC, STREAM
gl.createShader(gl.FRAGMENT_SHADER|gl.VERTEX_SHADER):glShader
создание шейдера заданного типа
gl.shaderSource(glShader shader, sourceString):void
установка текста шейдера
gl.compileShader(glShader shader):void
компиляция шейдера в инструкции GPU
gl.createProgram():glProgram
создание программы обработки
gl.attachShader(glProgram program, glShader shader):void
привязка шейдера к программе
gl.linkProgram(glProgram program):void
линковка скомпилированных шейдеров в исполняемый код программы
gl.getProgramParameter(glProgram program, gl.LINK_STATUS)
получение статуса сборки программы
gl.useProgram(glProgram program):void
установка готовой собранной программы для GPU
[FONT=Calibri]gl.getAttribLocation(glProgram program, nameString):reference

получение ссылки на атрибут где-то в собранной программе
gl.getUniformLocation(glProgram program, nameString):reference
получение ссылки на константу выполнения
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Arks
Arks
Topic Starter сообщение 30.11.2012, 3:23; Ответить: Arks
Сообщение #10


Предлагаю также тыкнуть на "спасибку" всем кому эта тема была интересна. А то, может, и зря старался...
Напомниаю - вопросы по WebGL, этому коду или технологии в ваших решениях можете смело задавать в skype
Буду рад ответить как новичкам, так и по вопросам расчета сложной оптики и освещения по законам геометрической оптики, дисперсии, дифракции, интерференции и расчету интенсивности и спектра отраженных лучей.

Планирую в дальшейшем расписать в теме законы преломления и отражения света приманительно к матричной алгебре если время до НГ позволит...
Тему открыл, но будет очень жесткая модерация.
Так что пишите свои пожелания по освещению вопросов WebGL
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
2 страниц V   1 2 >
Открыть тему
Тема закрыта
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0


Свернуть

> Похожие темы

  Тема Ответов Автор Просмотров Последний ответ
Открытая тема (нет новых ответов) Тема имеет прикрепленные файлыОптимизация видео Youtube 2019 + первые просмотры, трафик!
25 SeoVet 8325 7.4.2020, 22:22
автор: SeoVet
Горячая тема (нет новых ответов) Тема имеет прикрепленные файлыEdu-Cash.com - ПП под бурж студенческий траф, до 75% за первые заказы, до 35% за ребиллы, до 10% реферальных!
52 EduCash 24062 11.9.2019, 14:54
автор: Edu_Cash
Открытая тема (нет новых ответов) Edu-Cash.com - ПП под бурж студенческий траф, до 75% за первые,до 35% за ребиллы!
9 Edu Cash 2523 1.10.2018, 16:49
автор: -Edu Cash-
Открытая тема (нет новых ответов) Ваши первые действия после создания сайта
представьте что вы создали новый сайт
13 HavingingWorld 6719 30.5.2018, 0:05
автор: HavingingWorld
Открытая тема (нет новых ответов) DzenWap.ru - бонус 1000 рублей на первые 100 подписок.
7 SafeWap 3245 10.12.2016, 20:25
автор: SafeWap


 



RSS Текстовая версия Сейчас: 28.3.2024, 12:31
Дизайн