portaldacalheta.pt
  • Главни
  • Агиле Талент
  • Финансијски Процеси
  • Дизајн Бренда
  • Трендови
Технологија

3Д графика: Водич за ВебГЛ



Улазак у свет 3Д графике може бити врло застрашујући. Без обзира да ли желите само да направите интерактивни 3Д логотип или дизајнирате потпуно развијену игру, ако не знате принципе 3Д приказивања, заглавили сте користећи библиотеку која апстрахује много ствари.

Коришћење библиотеке може бити право средство и ЈаваСцрипт има невероватан опен соурце у облику тхрее.јс . Постоје неки недостаци коришћења унапред направљених решења:



  • Могу имати много функција које не планирате да користите. Величина умањене основне три.јс функције је око 500кБ, а све додатне функције (учитавање стварних датотека модела су једна од њих) чине корисни терет још већим. Пренос толико података само да би се на вашој веб локацији приказао предење логотипа био би губитак.
  • Додатни слој апстракције може отежати извођење једноставних модификација. Ваш креативни начин сенчења предмета на екрану може бити једноставан за примену или ће захтевати десетине сати рада да се уврсти у апстракције библиотеке.
  • Иако је библиотека у већини сценарија врло добро оптимизована, за ваш случај употребе може се изрезати пуно звукова. Рендерер може узроковати покретање одређених поступака милион пута на графичкој картици. Свако упутство уклоњено из такве процедуре значи да слабија графичка картица може без проблема да обрађује ваш садржај.

Чак и ако се одлучите за употребу библиотеке графичког материјала високог нивоа, поседовање основних знања о стварима испод хаубе омогућава вам да је ефикасније користите. Библиотеке такође могу имати напредне функције, попут ShaderMaterial у three.js. Познавање принципа приказивања графике омогућава вам да користите такве функције.



Илустрација 3Д АпееСцапе логотипа на ВебГЛ платну



Циљ нам је да укратко представимо све кључне концепте који стоје иза приказивања 3Д графике и употребе ВебГЛ-а за њихову примену. Видећете најчешће учињено, а то је приказивање и померање 3Д објеката у празном простору.

Тхе завршни код је доступан за вас да се разиђете и поиграте.



Представља 3Д моделе

Прво што би требало да разумете је како су представљени 3Д модели. Модел је направљен од мреже троуглова. Сваки троугао је представљен са три темена, за сваки од углова троугла. За темена су везана три најчешћа својства.

Вертек Поситион

Положај је најинтуитивније својство темена. То је положај у 3Д простору, представљен 3Д вектором координата. Ако знате тачне координате три тачке у свемиру, имали бисте све информације потребне за цртање једноставног троугла између њих. Да би модели изгледали стварно добро приликом приказивања, постоји још неколико ствари које треба пружити приказивачу.



Вертек Нормал

Сфере са истим жичаним оквиром, које имају примењено равно и глатко сенчење

Размотрите два горња модела. Састоје се од истих положаја темена, али изгледају потпуно другачије када се прикажу. Како је то могуће?



Поред тога што ћемо рендереру рећи где желимо да се налази врх, можемо му дати и наговештај како је површина нагнута у том тачном положају. Наговештај је у облику нормале површине у тој одређеној тачки на моделу, представљеној 3Д вектором. Следећа слика би требало да вам пружи описнији поглед на то како се то поступа.

Поређење између нормала за равно и глатко сенчење



Лева и десна површина одговарају левој и десној кугли на претходној слици. Црвене стрелице представљају нормале које су одређене за врх, док плаве стрелице представљају израчуне приказивача како нормала треба да тражи све тачке између темена. Слика приказује демонстрацију за 2Д простор, али исти принцип важи и за 3Д.

Нормалан је наговештај како ће светла осветљавати површину. Што је смер светлосног зрака ближи нормалном, тачка је светлија. Постепене промене у нормалном смеру изазивају градијент светлости, док нагле промене без промена између њих узрокују површине са сталним осветљењем преко њих и нагле промене осветљености између њих.



Координате текстуре

Последње значајно својство су координате текстуре, које се обично називају УВ мапирање. Имате модел и текстуру коју желите да примените на њега. Текстура на себи има различита подручја која представљају слике које желимо да применимо на различите делове модела. Мора постојати начин да се означи који троугао треба представити којим делом текстуре. Ту долази мапирање текстуре.

За сваки врх означавамо две координате, У и В. Те координате представљају положај на текстури, при чему У представља хоризонталну осу, а В вертикалну осу. Вредности нису у пикселима, већ у процентуалном положају на слици. Доњи леви угао слике представљен је са две нуле, док је горњи десни приказан са две.

Троугао се само осликава узимајући УВ координате сваког темена у троуглу и примењујући слику која је забележена између тих координата на текстуру.

Демонстрација УВ мапирања, са истакнутим једним закрпом и шавовима видљивим на моделу

На горњој слици можете видети демонстрацију УВ мапирања. Сферни модел је узет и исечен на делове који су довољно мали да се спљоште на 2Д површину. Шавови на којима су направљени резови обележени су дебљим линијама. Једна од закрпа је истакнута, тако да можете лепо видети како се ствари подударају. Такође можете видети како шав кроз средину осмеха ставља делове уста у два различита закрпа.

Жичани оквири нису део текстуре, већ се само прекривају преко слике да бисте могли видети како се ствари међусобно мапирају.

Учитавање ОБЈ модела

Веровали или не, ово је све што треба да знате да бисте креирали свој властити једноставан модел утоваривача. Тхе ОБЈ формат датотеке је довољно једноставан за имплементацију парсера у неколико редова кода.

Датотека наводи положаје врхова у v формат, са опционим четвртим пловком, који ћемо занемарити, како би ствари биле једноставније. Нормали темена су представљени слично са vn . Коначно, координате текстуре су представљене са vt , са опционим трећим пловком који ћемо занемарити. У сва три случаја пловци представљају одговарајуће координате. Ова три својства се акумулирају у три низа.

Лица су представљена групама темена. Сваки врх је представљен индексом сваког од својстава, при чему индекси почињу на 1. Постоје различити начини на које се то представља, али ми ћемо се држати f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 формату, захтевајући да се обезбеде сва три својства и ограничавајући број врхова по лицу на три. Сва ова ограничења се раде како би лоадер био што једноставнији, јер све остале опције захтевају неку додатну тривијалну обраду пре него што постану у формату који ВебГЛ воли.

Поставили смо пуно захтева за наш уређај за учитавање датотека. То може звучати ограничено, али апликације за 3Д моделирање имају тенденцију да вам дају могућност постављања тих ограничења приликом извоза модела као ОБЈ датотеке.

Следећи код анализира низ који представља ОБЈ датотеку и креира модел у облику низа лица.

function Geometry (faces) [] // Parses an OBJ file, passed as a string Geometry.parseOBJ = function (src) { var POSITION = /^vs+([d.+-eE]+)s+([d.+-eE]+)s+([d.+-eE]+)/ var NORMAL = /^vns+([d.+-eE]+)s+([d.+-eE]+)s+([d.+-eE]+)/ var UV = /^vts+([d.+-eE]+)s+([d.+-eE]+)/ var FACE = /^fs+(-?d+)/(-?d+)/(-?d+)s+(-?d+)/(-?d+)/(-?d+)s+(-?d+)/(-?d+)/(-?d+)(?:s+(-?d+)/(-?d+)/(-?d+))?/ lines = src.split(' ') var positions = [] var uvs = [] var normals = [] var faces = [] lines.forEach(function (line) { // Match each line of the file against various RegEx-es var result if ((result = POSITION.exec(line)) != null) { // Add new vertex position positions.push(new Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]))) } else if ((result = NORMAL.exec(line)) != null) { // Add new vertex normal normals.push(new Vector3(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]))) } else if ((result = UV.exec(line)) != null) { // Add new texture mapping point uvs.push(new Vector2(parseFloat(result[1]), 1 - parseFloat(result[2]))) } else if ((result = FACE.exec(line)) != null) { // Add new face var vertices = [] // Create three vertices from the passed one-indexed indices for (var i = 1; i <10; i += 3) { var part = result.slice(i, i + 3) var position = positions[parseInt(part[0]) - 1] var uv = uvs[parseInt(part[1]) - 1] var normal = normals[parseInt(part[2]) - 1] vertices.push(new Vertex(position, normal, uv)) } faces.push(new Face(vertices)) } }) return new Geometry(faces) } // Loads an OBJ file from the given URL, and returns it as a promise Geometry.loadOBJ = function (url) { return new Promise(function (resolve) { var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { if (xhr.readyState == XMLHttpRequest.DONE) { resolve(Geometry.parseOBJ(xhr.responseText)) } } xhr.open('GET', url, true) xhr.send(null) }) } function Face (vertices) this.vertices = vertices function Vertex (position, normal, uv) new Vector3() this.normal = normal function Vector3 (x, y, z) function Vector2 (x, y) 0 this.y = Number(y)

Тхе Geometry структура садржи тачне податке потребне за слање модела на графичку картицу на обраду. Пре него што то учините, вероватно бисте желели да имате могућност померања модела по екрану.

Извођење просторних трансформација

Све тачке у моделу који смо учитали су у односу на његов координатни систем. Ако желимо да преведемо, ротирамо и прилагодимо модел, све што треба да урадимо је да извршимо ту операцију на његовом координатном систему. Координатни систем А, у односу на координатни систем Б, дефинисан је положајем његовог средишта као вектор p_ab и вектором за сваку од његових оса, x_ab, y_ab и z_ab, представља правац те осе. Дакле, ако се тачка помери за 10 на x ос координатног система А, тада ће се - у координатном систему Б - померити у смеру x_ab, помножено са 10.

Све ове информације чувају се у следећем матричном облику:

x_ab.x y_ab.x z_ab.x p_ab.x x_ab.y y_ab.y z_ab.y p_ab.y x_ab.z y_ab.z z_ab.z p_ab.z 0 0 0 1

Ако желимо да трансформишемо 3Д вектор q, морамо само помножити матрицу трансформације са вектором:

q.x q.y q.z 1

Ово доводи до померања тачке за q.x дуж новог x ос, за q.y дуж новог y оси, а по q.z дуж новог z ос. Коначно, тачка се додатно помера за p вектор, што је разлог зашто један користимо као завршни елемент множења.

Велика предност коришћења ових матрица је чињеница да ако имамо више трансформација које треба извршити на врху, можемо их спојити у једну трансформацију множењем њихових матрица, пре трансформације самог темена.

Постоје разне трансформације које се могу извести, а ми ћемо погледати кључне.

Нема трансформације

Ако се не догоди трансформација, p вектор је нулти вектор, x вектор је [1, 0, 0], y је [0, 1, 0], и z је [0, 0, 1]. Од сада ћемо те вредности називати подразумеваним вредностима за ове векторе. Примена ових вредности даје нам матрицу идентитета:

1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1

Ово је добра полазна основа за уланчавање трансформација.

Превод

Трансформација оквира за превод

Када изводимо превод, тада сви вектори осим p вектор имају задате вредности. Резултат је следећа матрица:

1 0 0 p.x 0 1 0 p.y 0 0 1 p.z 0 0 0 1

Скалирање

Трансформација оквира за скалирање

Скалирање модела значи смањење количине која свака координата доприноси положају тачке. Не постоји једнолико померање узроковано скалирањем, тако да p вектор задржава своју подразумевану вредност. Подразумеване векторе осе треба помножити са њиховим одговарајућим факторима скалирања, што резултира следећом матрицом:

s_x 0 0 0 0 s_y 0 0 0 0 s_z 0 0 0 0 1

Овде s_x, s_y и s_z представљају скалирање примењено на сваку осу.

Ротација

Трансформација оквира за ротацију око З осе

Горња слика приказује шта се дешава када ротирамо координатни оквир око З осе.

Ротација резултира неуједначеним помаком, тако да p вектор задржава своју подразумевану вредност. Сада ствари постају мало замршеније. Ротације узрокују кретање дуж одређене осе у оригиналном координатном систему у другом смеру. Дакле, ако ротирамо координатни систем за 45 степени око З осе, крећући се дуж x ос оригиналног координатног система узрокује кретање у дијагоналном смеру између x и y ос у новом координатном систему.

Да би ствари биле једноставне, показаћемо вам само како матрице трансформације траже ротације око главних осе.

Around X: 1 0 0 0 0 cos(phi) sin(phi) 0 0 -sin(phi) cos(phi) 0 0 0 0 1 Around Y: cos(phi) 0 sin(phi) 0 0 1 0 0 -sin(phi) 0 cos(phi) 0 0 0 0 1 Around Z: cos(phi) -sin(phi) 0 0 sin(phi) cos(phi) 0 0 0 0 1 0 0 0 0 1

Имплементација

Све ово се може применити као класа која чува 16 бројева, чувајући матрице у а колона-главни ред .

function Transformation () { // Create an identity transformation this.fields = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] } // Multiply matrices, to chain transformations Transformation.prototype.mult = function (t) { var output = new Transformation() for (var row = 0; row <4; ++row) { for (var col = 0; col < 4; ++col) { var sum = 0 for (var k = 0; k < 4; ++k) { sum += this.fields[k * 4 + row] * t.fields[col * 4 + k] } output.fields[col * 4 + row] = sum } } return output } // Multiply by translation matrix Transformation.prototype.translate = function (x, y, z) // Multiply by scaling matrix Transformation.prototype.scale = function (x, y, z) // Multiply by rotation matrix around X axis Transformation.prototype.rotateX = function (angle) // Multiply by rotation matrix around Y axis Transformation.prototype.rotateY = function (angle) // Multiply by rotation matrix around Z axis Transformation.prototype.rotateZ = function (angle)

Гледање кроз камеру

Ево кључног дела представљања предмета на екрану: камере. Постоје две кључне компоненте камере; наиме његов положај и начин на који пројектује посматране објекте на екран.

Положајем камере се управља једним једноставним триком. Не постоји визуелна разлика између померања камере за метар унапред и померања целог света за метар уназад. Тако да, природно, радимо ово друго, примењујући инверзу матрице као трансформацију.

Друга кључна компонента је начин на који се посматрани објекти пројектују на сочиво. У ВебГЛ-у се све видљиво на екрану налази у оквиру. Кутија се креће између -1 и 1 на свакој оси. Све видљиво је у тој кутији. Исти приступ матрицама трансформације можемо користити за стварање матрице пројекције.

Ортографска пројекција

Правокутни простор се трансформише у одговарајуће димензије међуспремника оквира помоћу ортографске пројекције

Најједноставнија пројекција је правописна пројекција . Узимате оквир у простору који означава ширину, висину и дубину, уз претпоставку да је његово средиште у нултој позицији. Тада пројекција мења величину оквира тако да се уклопи у претходно описано поље унутар којег ВебГЛ посматра објекте. С обзиром да желимо да сваку димензију променимо на две, сваку осу увећамо за 2/size, при чему size је димензија одговарајуће осе. Мала напомена је чињеница да множимо З осу са негативом. То је учињено јер желимо да окренемо правац те димензије. Коначна матрица има овај облик:

2/width 0 0 0 0 2/height 0 0 0 0 -2/depth 0 0 0 0 1

Пројекција перспективе

Фрустум се трансформише у одговарајуће димензије бафера помоћу пројекције у перспективи

Нећемо пролазити кроз детаље како је дизајнирана ова пројекција, већ само користите коначна формула , што је до сада прилично стандардно. Можемо га поједноставити постављањем пројекције у нулти положај на оси к и и, чинећи десну / леву и горњу / доњу границу једнаком width/2 и height/2 редом. Параметри n и f представљају near и far равни одсецања, које су најмања и највећа удаљеност коју тачка може да ухвати камера. Они су представљени паралелним страницама фрустум на горњој слици.

Перспективна пројекција је обично представљена са видно поље (користићемо вертикални), однос ширине и висине , и близину и даљину авиона. Те информације се могу користити за израчунавање width и height, а затим се матрица може креирати из следећег шаблона:

2*n/width 0 0 0 0 2*n/height 0 0 0 0 (f+n)/(n-f) 2*f*n/(n-f) 0 0 -1 0

За израчунавање ширине и висине могу се користити следеће формуле:

height = 2 * near * Math.tan(fov * Math.PI / 360) width = aspectRatio * height

ФОВ (видно поље) представља вертикални угао који камера снима објективом. Однос ширине и висине представља однос између ширине и висине слике и заснован је на димензијама екрана на који приказујемо.

Имплементација

Сада можемо представити камеру као класу која чува положај камере и матрицу пројекције. Такође морамо знати како израчунати инверзне трансформације. Решавање опште матричне инверзије може бити проблематично, али постоји поједностављени приступ за наш посебан случај.

function Camera () { this.position = new Transformation() this.projection = new Transformation() } Camera.prototype.setOrthographic = function (width, height, depth) { this.projection = new Transformation() this.projection.fields[0] = 2 / width this.projection.fields[5] = 2 / height this.projection.fields[10] = -2 / depth } Camera.prototype.setPerspective = function (verticalFov, aspectRatio, near, far) { var height_div_2n = Math.tan(verticalFov * Math.PI / 360) var width_div_2n = aspectRatio * height_div_2n this.projection = new Transformation() this.projection.fields[0] = 1 / height_div_2n this.projection.fields[5] = 1 / width_div_2n this.projection.fields[10] = (far + near) / (near - far) this.projection.fields[10] = -1 this.projection.fields[14] = 2 * far * near / (near - far) this.projection.fields[15] = 0 } Camera.prototype.getInversePosition = function () { var orig = this.position.fields var dest = new Transformation() var x = orig[12] var y = orig[13] var z = orig[14] // Transpose the rotation matrix for (var i = 0; i <3; ++i) { for (var j = 0; j < 3; ++j) { dest.fields[i * 4 + j] = orig[i + j * 4] } } // Translation by -p will apply R^T, which is equal to R^-1 return dest.translate(-x, -y, -z) }

Ово је последњи комад који нам треба пре него што почнемо да цртамо ствари на екрану.

Цртање објекта помоћу ВебГЛ графичког цјевовода

Најједноставнија површина коју можете нацртати је троугао. У ствари, већина ствари које цртате у 3Д простору састоје се од великог броја троуглова.

Основни поглед на кораке графичког цевовода

Прво што треба да разумете је како је екран представљен у ВебГЛ-у. То је 3Д простор, распона између -1 и 1 на Икс , И. , и са ос. Подразумевано ово са оса се не користи, али вас занима 3Д графика, па ћете је одмах желети омогућити.

Имајући то на уму, следе три корака потребна за цртање троугла на ову површину.

Можете дефинирати три темена која би представљала троугао који желите нацртати. Те податке сериализујете и шаљете на ГПУ (јединицу за обраду графике). Уз доступан читав модел, то можете учинити за све троуглове у моделу. Положаји темена које дајете налазе се у локалном координатном простору модела који сте учитали. Једноставно речено, позиције које наведете су тачне из датотеке, а не оне које добијете након извођења матричних трансформација.

Сад кад сте врхове дали ГПУ-у, кажете ГПУ-у коју логику да користите приликом постављања врхова на екран. Овај корак ће се користити за примену наших матричних трансформација. ГПУ је веома добар у множењу пуно 4к4 матрица, па ћемо ту способност добро искористити.

У последњем кораку, ГПУ ће растеризирати тај троугао. Растеризација је поступак узимања векторске графике и одређивања који пиксели екрана треба да буду обојени да би се тај објект векторске графике приказао. У нашем случају, ГПУ покушава да утврди који се пиксели налазе унутар сваког троугла. За сваки пиксел, ГПУ ће вас питати у коју боју желите да буде обојен.

Ово су четири елемента потребна за цртање било чега што желите и они су најједноставнији пример а графички цевовод . Следи поглед на сваког од њих и једноставна примена.

Подразумевани фрамебуффер

Најважнији елемент за ВебГЛ апликацију је ВебГЛ контекст. Можете му приступити са gl = canvas.getContext('webgl') или користити 'experimental-webgl' као резервни случај, у случају да тренутно коришћени прегледач још увек не подржава све ВебГЛ функције. Тхе canvas који смо поменули је ДОМ елемент платна на којем желимо да цртамо. Контекст садржи много ствари, међу којима је и подразумевани фрамебуффер.

Можете слободно описати фрамебуффер као било који бафер (објекат) на којем можете цртати. Подразумевано, буффер фраме меморише боју за сваки пиксел платна на који је везан контекст ВебГЛ. Као што је описано у претходном одељку, када цртамо на баферу, сваки пиксел се налази између -1 и 1 на Икс и И. ос. Нешто што смо такође поменули је чињеница да ВебГЛ подразумевано не користи са ос. Та функционалност се може омогућити покретањем gl.enable(gl.DEPTH_TEST). Сјајно, али шта је дубински тест?

Омогућавање теста дубине омогућава пикселу да чува и боју и дубину. Дубина је са координата тог пиксела. Након што нацртате пиксел на одређеној дубини са , да бисте ажурирали боју тог пиксела, потребно је да нацртате у са положај који је ближи камери. У супротном, покушај извлачења биће занемарен. Ово омогућава илузију 3Д-а, јер ће цртање објеката који се налазе иза других објеката довести до тога да ти објекти буду зачепљени објектима испред себе.

Све извлачења која изводите остају на екрану док им не кажете да се уклоне. Да бисте то урадили, морате назвати gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT). Ово брише и тампон боје и дубине. Да бисте одабрали боју на коју су постављени очишћени пиксели, користите gl.clearColor(red, green, blue, alpha).

Направимо приказивач који користи платно и брише га на захтев:

function Renderer (canvas) Renderer.prototype.setClearColor = function (red, green, blue) { gl.clearColor(red / 255, green / 255, blue / 255, 1) } Renderer.prototype.getContext = function () { return this.gl } Renderer.prototype.render = function () gl.DEPTH_BUFFER_BIT) var renderer = new Renderer(document.getElementById('webgl-canvas')) renderer.setClearColor(100, 149, 237) loop() function loop () { renderer.render() requestAnimationFrame(loop) }

Ако ову скрипту прикачите на следећи ХТМЛ, добићете светло плави правоугаоник на екрану

requestAnimationFrame

Тхе N цалл доводи до поновног позива петље чим се заврши рендеровање претходног оквира и заврши са свим руковањем догађајима.

Објекти ме успремника вертекса

Прво што треба да урадите је да дефинишете темена која желите да нацртате. То можете учинити тако што ћете их описати путем вектора у 3Д простору. Након тога, желите да те податке преместите у ГПУ РАМ, стварањем новог Вертек Буффер објект (ФЕБ).

ДО Објекат пуфера уопште је објекат који на ГПУ чува низ меморијских делова. То што је ВБО само означава за шта ГПУ може да користи меморију. Већину времена, Буффер објекти које креирате биће ВБО.

ВБО можете попунити узимајући све 3N врхове које имамо и стварање низа лебдећих са 2N елементи за положај темена и нормалне ВБО темена и Geometry за координате текстуре ВБО. Свака група од три пловка, или два пловка за УВ координате, представљају појединачне координате темена. Затим прослеђујемо ове низове ГПУ-у и наши врхови су спремни за остатак цевовода.

Пошто су подаци сада на ГПУ РАМ-у, можете их избрисати из РАМ-а опште намене. Односно, уколико не желите касније да га измените и поново отпремите. Сваку модификацију треба пратити преносом, јер се модификације у нашим ЈС низима не примењују на ВБО-ове у стварном РАМ-у ГПУ-а.

Испод је пример кода који пружа све описане функционалности. Важну напомену је чињеница да променљиве ускладиштене на ГПУ-у нису сакупљено смеће. То значи да их морамо ручно избрисати када више не желимо да их користимо. Само ћемо вам дати пример како се то овде ради и нећемо се даље фокусирати на тај концепт. Брисање променљивих са ГПУ-а је неопходно само ако планирате да престанете да користите одређену геометрију током целог програма.

Такође смо додали сериализацију у наш Geometry.prototype.vertexCount = function () { return this.faces.length * 3 } Geometry.prototype.positions = function () { var answer = [] this.faces.forEach(function (face) { face.vertices.forEach(function (vertex) { var v = vertex.position answer.push(v.x, v.y, v.z) }) }) return answer } Geometry.prototype.normals = function () { var answer = [] this.faces.forEach(function (face) { face.vertices.forEach(function (vertex) { var v = vertex.normal answer.push(v.x, v.y, v.z) }) }) return answer } Geometry.prototype.uvs = function () { var answer = [] this.faces.forEach(function (face) { face.vertices.forEach(function (vertex) { var v = vertex.uv answer.push(v.x, v.y) }) }) return answer } //////////////////////////////// function VBO (gl, data, count) { // Creates buffer object in GPU RAM where we can store anything var bufferObject = gl.createBuffer() // Tell which buffer object we want to operate on as a VBO gl.bindBuffer(gl.ARRAY_BUFFER, bufferObject) // Write the data, and set the flag to optimize // for rare changes to the data we're writing gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW) this.gl = gl this.size = data.length / count this.count = count this.data = bufferObject } VBO.prototype.destroy = function () { // Free memory that is occupied by our buffer object this.gl.deleteBuffer(this.data) } класа и елементи у њој.

VBO

Тхе gl тип података генерише ВБО у прослеђеном ВебГЛ контексту, на основу низа прослеђеног као други параметар.

Можете видети три позива на createBuffer() контекст. Тхе bindBuffer() позив ствара бафер. Тхе ARRAY_BUFFER цалл говори ВебГЛ државном аутомату да користи ову специфичну меморију као тренутни ВБО (bufferData()) за све будуће операције, док се не каже другачије. Након тога, вредност тренутног ВБО поставили смо на пружене податке, са deleteBuffer() .

Такође пружамо методу уништавања која помоћу нас брише наш објекат међуспремника из ГПУ РАМ-а function Mesh (gl, geometry) { var vertexCount = geometry.vertexCount() this.positions = new VBO(gl, geometry.positions(), vertexCount) this.normals = new VBO(gl, geometry.normals(), vertexCount) this.uvs = new VBO(gl, geometry.uvs(), vertexCount) this.vertexCount = vertexCount this.position = new Transformation() this.gl = gl } Mesh.prototype.destroy = function () { this.positions.destroy() this.normals.destroy() this.uvs.destroy() } .

Можете користити три ВБО-а и трансформацију да бисте описали сва својства мреже, заједно са њеним положајем.

Geometry.loadOBJ('/assets/model.obj').then(function (geometry) { var mesh = new Mesh(gl, geometry) console.log(mesh) mesh.destroy() })

Као пример, ево како можемо учитати модел, похранити његова својства у мрежу, а затим га уништити:

attribute

Схадерс

Следи претходно описани поступак у два корака померања тачака у жељене положаје и бојења свих појединачних пиксела. Да бисмо то урадили, напишемо програм који се много пута покреће на графичкој картици. Овај програм се обично састоји од најмање два дела. Први део је а Вертек Схадер , који се покреће за сваки врх и излази тамо где бисмо требали поставити врх на екран, између осталог. Други део је Фрагмент Схадер , који се покреће за сваки пиксел који троугао покрива на екрану и даје боју у коју би пиксел требало да буде обојен.

Вертек Схадерс

Рецимо да желите да имате модел који се креће лево и десно по екрану. У наивном приступу, могли бисте ажурирати положај сваког темена и послати га на ГПУ. Тај процес је скуп и спор. Као алтернативу, дали бисте програм за ГПУ који ће се покретати за сваки врх и све те операције радити паралелно са процесором који је направљен за обављање управо тог посла. То је улога а вертек схадер .

Вертек схадер је део цевовода за приказивање који обрађује појединачне врхове. Позив сенку за сенке прима један врх и даје један врх након примене свих могућих трансформација у врх.

Шејдери су написани на ГЛСЛ-у. У овом језику има пуно јединствених елемената, али већина синтаксе је врло слична Ц, тако да би већини људи требало да буде разумљива.

Постоје три врсте променљивих које улазе и излазе из верде сенкера и све оне служе за одређену употребу:

  • uniform - То су улази који садрже специфична својства темена. Претходно смо описали положај темена као атрибут, у облику вектора са три елемента. Атрибуте можете гледати као вредности које описују један врх.
  • uniform - То су улази који су исти за сваки врх у истом позиву за приказивање. Рецимо да желимо да можемо да померамо свој модел, дефинишући матрицу трансформације. Можете користити varying променљива да то опише. Можете усмерити и ресурсе на ГПУ-у, попут текстура. На униформе можете гледати као на вредности које описују модел или део модела.
  • attribute vec3 position; attribute vec3 normal; attribute vec2 uv; uniform mat4 model; uniform mat4 view; uniform mat4 projection; varying vec3 vNormal; varying vec2 vUv; void main() { vUv = uv; vNormal = (model * vec4(normal, 0.)).xyz; gl_Position = projection * view * model * vec4(position, 1.); } - То су излази које прослеђујемо у сенчење фрагмената. Будући да за троугао врхова постоји потенцијално хиљаде пиксела, сваки пиксел ће добити интерполирану вредност за ову променљиву, у зависности од положаја. Дакле, ако један врх пошаље 500 као излаз, а други 100, пиксел који се налази у средини између њих добиће 300 као улаз за ту променљиву. Варијације можете гледати као вредности које описују површине између врхова.

Дакле, рецимо да желите да направите осенчивач вертекса који прима положај, нормалне и УВ координате за сваки врх, и положај, приказ (инверзни положај камере) и матрицу пројекције за сваки приказани објекат. Рецимо да такође желите да сликате појединачне пикселе на основу њихових УВ координата и њихових нормалних вредности. 'Како би изгледао тај код?' можете питати.

main

Већина елемената овде треба да буде саморазумљива. Кључна ствар коју треба приметити је чињеница да у varying нема повратних вредности функцију. Све вредности које бисмо желели да вратимо додељују се или gl_Position променљиве, или на посебне променљиве. Овде додељујемо vec4, што је четвородимензионални вектор, при чему последња димензија увек треба бити постављена на једну. Још једна необична ствар коју бисте могли приметити је начин на који конструишемо vec4 ван вектора положаја. Можете конструисати float коришћењем четири vec2 с, две varying с или било које друге комбинације која резултира у четири елемента. Постоји много наизглед чудних кастинга који имају савршеног смисла када се упознате са матрицама трансформације.

Такође можете видети да овде можемо изузетно лако да изведемо матричне трансформације. ГЛСЛ је специјално направљен за ову врсту посла. Излазна позиција израчунава се множењем матрице пројекције, погледа и модела и применом на положај. Излазни нормалан се управо трансформише у светски простор. Објаснићемо касније зашто смо се ту зауставили са нормалним трансформацијама.

За сада ћемо бити једноставни и прећи ћемо на сликање појединачних пиксела.

Фрагмент Схадерс

ДО сенчник фрагмената је корак након растеризације у графичком цевоводу. Генерише боју, дубину и друге податке за сваки пиксел предмета који се слика.

Принципи који стоје иза примене фрагментних сенкира врло су слични вертексним сенкерима. Постоје три главне разлике:

  • Нема више attribute излази и varying улази су замењени са gl_FragColor улази. Управо смо кренули даље у нашем цевоводу, а ствари које су излаз у вертек схадеру сада су улази у схадер фрагмената.
  • Наш једини излаз сада је vec4, што је #ifdef GL_ES precision highp float; #endif varying vec3 vNormal; varying vec2 vUv; void main() { vec2 clampedUv = clamp(vUv, 0., 1.); gl_FragColor = vec4(clampedUv, 1., 1.); } . Елементи представљају црвену, зелену, плаву и алфа (РГБА), са променљивим у опсегу од 0 до 1. Требало би да задржите алфа вредност 1, осим ако не радите транспарентност. Транспарентност је ипак прилично напредан концепт, па ћемо се држати непрозирних предмета.
  • На почетку сенчења фрагмената треба да подесите прецизност пловка, што је важно за интерполације. У скоро свим случајевима само се држите линија из следећег сенкера.

Имајући то на уму, лако можете да напишете сенку која осликава црвени канал на основу У положаја, зелени на основу В положаја и поставља плави канал на максимум.

clamp

Функција function ShaderProgram (gl, vertSrc, fragSrc) { var vert = gl.createShader(gl.VERTEX_SHADER) gl.shaderSource(vert, vertSrc) gl.compileShader(vert) if (!gl.getShaderParameter(vert, gl.COMPILE_STATUS)) { console.error(gl.getShaderInfoLog(vert)) throw new Error('Failed to compile shader') } var frag = gl.createShader(gl.FRAGMENT_SHADER) gl.shaderSource(frag, fragSrc) gl.compileShader(frag) if (!gl.getShaderParameter(frag, gl.COMPILE_STATUS)) { console.error(gl.getShaderInfoLog(frag)) throw new Error('Failed to compile shader') } var program = gl.createProgram() gl.attachShader(program, vert) gl.attachShader(program, frag) gl.linkProgram(program) if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error(gl.getProgramInfoLog(program)) throw new Error('Failed to link program') } this.gl = gl this.position = gl.getAttribLocation(program, 'position') this.normal = gl.getAttribLocation(program, 'normal') this.uv = gl.getAttribLocation(program, 'uv') this.model = gl.getUniformLocation(program, 'model') this.view = gl.getUniformLocation(program, 'view') this.projection = gl.getUniformLocation(program, 'projection') this.vert = vert this.frag = frag this.program = program } // Loads shader files from the given URLs, and returns a program as a promise ShaderProgram.load = function (gl, vertUrl, fragUrl) { return Promise.all([loadFile(vertUrl), loadFile(fragUrl)]).then(function (files) { return new ShaderProgram(gl, files[0], files[1]) }) function loadFile (url) { return new Promise(function (resolve) { var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { if (xhr.readyState == XMLHttpRequest.DONE) { resolve(xhr.responseText) } } xhr.open('GET', url, true) xhr.send(null) }) } } само ограничава све пловке у објекту да буду унутар задатих ограничења. Остатак кода би требао бити прилично једноставан.

Имајући све ово на уму, преостало је само да се ово примени у ВебГЛ-у.

Комбиновање сенки у програм

Следећи корак је комбиновање шејдера у програм:

ShaderProgram.prototype.use = function () { this.gl.useProgram(this.program) }

Нема много шта да се каже о ономе што се овде догађа. Сваком схадеру се додељује низ као извор и компајлира, након чега проверавамо да ли је било грешака у компилацији. Затим креирамо програм повезивањем ова два сендера. На крају, чувамо показиваче на све релевантне атрибуте и униформе за потомство.

Заправо цртање модела

На крају, али не најмање важно, цртате модел.

ц++ објектна датотека

Прво одаберете програм за сенчење који желите да користите.

Transformation.prototype.sendToGpu = function (gl, uniform, transpose) gl.uniformMatrix4fv(uniform, transpose Camera.prototype.use = function (shaderProgram) { this.projection.sendToGpu(shaderProgram.gl, shaderProgram.projection) this.getInversePosition().sendToGpu(shaderProgram.gl, shaderProgram.view) }

Затим шаљете све униформе повезане са камером на ГПУ. Ове униформе се мењају само једном по промени или кретању камере.

VBO.prototype.bindToAttribute = function (attribute) { var gl = this.gl // Tell which buffer object we want to operate on as a VBO gl.bindBuffer(gl.ARRAY_BUFFER, this.data) // Enable this attribute in the shader gl.enableVertexAttribArray(attribute) // Define format of the attribute array. Must match parameters in shader gl.vertexAttribPointer(attribute, this.size, gl.FLOAT, false, 0, 0) }

На крају, узимате трансформације и ВБО и додељујете их униформама, односно атрибутима. Будући да се то мора урадити за сваки ВБО, можете створити његово везивање података као метод.

drawArrays()

Затим униформи доделите низ од три пловка. Свака врста униформе има другачији потпис, па документација и још документација су вам пријатељи овде. На крају, нацртате низ троугла на екрану. Ти реци позиву за цртање TRIANGLES од којег врха почети и колико врхова нацртати. Први прослеђени параметар говори ВебГЛ-у како ће тумачити низ врхова. Коришћење POINTS узима три по три темена и за сваки триплет црта троугао. Коришћење Mesh.prototype.draw = function (shaderProgram) { this.positions.bindToAttribute(shaderProgram.position) this.normals.bindToAttribute(shaderProgram.normal) this.uvs.bindToAttribute(shaderProgram.uv) this.position.sendToGpu(this.gl, shaderProgram.model) this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertexCount) } само нацртао тачку за сваки пролазни врх. Постоји много више опција, али није потребно откривати све одједном. Испод је код за цртање објекта:

Renderer.prototype.setShader = function (shader) { this.shader = shader } Renderer.prototype.render = function (camera, objects) { this.gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) var shader = this.shader if (!shader) { return } shader.use() camera.use(shader) objects.forEach(function (mesh) { mesh.draw(shader) }) }

Приказивање треба мало проширити да би се прилагодили свим додатним елементима којима треба руковати. Требало би бити могуће приложити програм за сенчење и приказати низ објеката на основу тренутне позиције камере.

var renderer = new Renderer(document.getElementById('webgl-canvas')) renderer.setClearColor(100, 149, 237) var gl = renderer.getContext() var objects = [] Geometry.loadOBJ('/assets/sphere.obj').then(function (data) { objects.push(new Mesh(gl, data)) }) ShaderProgram.load(gl, '/shaders/basic.vert', '/shaders/basic.frag') .then(function (shader) { renderer.setShader(shader) }) var camera = new Camera() camera.setOrthographic(16, 10, 10) loop() function loop () { renderer.render(camera, objects) requestAnimationFrame(loop) }

Можемо комбиновати све елементе које морамо коначно да нацртамо на екрану:

#ifdef GL_ES precision highp float; #endif varying vec3 vNormal; varying vec2 vUv; void main() { vec3 brown = vec3(.54, .27, .07); gl_FragColor = vec4(brown, 1.); }

Предмет нацртан на платну, бојама у зависности од УВ координата

Ово изгледа помало случајно, али можете видети различите мрље сфере на основу тога где се налазе на УВ мапи. Можете да промените сенку да бисте предмет обојили у смеђе. Само подесите боју за сваки пиксел да буде РГБА за смеђу:

#ifdef GL_ES precision highp float; #endif varying vec3 vNormal; varying vec2 vUv; void main() { vec3 brown = vec3(.54, .27, .07); vec3 sunlightDirection = vec3(-1., -1., -1.); float lightness = -clamp(dot(normalize(vNormal), normalize(sunlightDirection)), -1., 0.); gl_FragColor = vec4(brown * lightness, 1.); }

Смеђи предмет нацртан на платну

Не изгледа баш уверљиво. Изгледа да сцени требају неки ефекти сенчења.

Додавање светлости

Светла и сенке су алати који нам омогућавају да сагледамо облик предмета. Светла се могу наћи у многим облицима и величинама: рефлектори који сијају у једном конусу, сијалице које шире светлост у свим правцима, и што је најзанимљивије, сунце које је толико далеко да сва светлост којом нас обасјава зрачи, за све намере и сврхе, у истом правцу.

Сунчева светлост звучи као да је најједноставнија за примену, јер све што треба да пружите је правац у коме се сви зраци шире. За сваки пиксел који нацртате на екрану проверите угао под којим светлост погађа предмет. Овде улазе нормалне површине.

Демонстрација углова између светлосних зрака и површинских нормала, како за равно тако и за глатко сенчење

Можете видети све светлосне зраке како теку у истом смеру и ударају у површину под различитим угловима, који се заснивају на углу између светлосног зрака и површине нормалне. Што се више поклапају, светло је јаче.

Ако изведете тачкасти производ између нормализованих вектора за светлосни зрак и површинске нормале, добићете -1 ако зрак погоди површину савршено окомито, 0 ако је зрак паралелан површини и 1 ако га осветљава из супротна страна. Дакле, све између 0 и 1 не би требало да додаје светло, док би бројеви између 0 и -1 требали постепено повећавати количину светлости која удара о објекат. Ово можете да тестирате додавањем фиксне лампице у код сенкера.

#ifdef GL_ES precision highp float; #endif varying vec3 vNormal; varying vec2 vUv; void main() { vec3 brown = vec3(.54, .27, .07); vec3 sunlightDirection = vec3(-1., -1., -1.); float lightness = -clamp(dot(normalize(vNormal), normalize(sunlightDirection)), -1., 0.); float ambientLight = 0.3; lightness = ambientLight + (1. - ambientLight) * lightness; gl_FragColor = vec4(brown * lightness, 1.); }

Смеђи предмет са сунчевом светлошћу

Сунце смо поставили да сија у смеру напред-лево-доле. Можете видети колико је сенчење глатко, иако је модел врло назубљен. Такође можете приметити колико је доња лева страна тамна. Можемо додати ниво амбијенталне светлости, што ће подручје у сенци учинити светлијим.

#ifdef GL_ES precision highp float; #endif uniform vec3 lightDirection; uniform float ambientLight; varying vec3 vNormal; varying vec2 vUv; void main() { vec3 brown = vec3(.54, .27, .07); float lightness = -clamp(dot(normalize(vNormal), normalize(lightDirection)), -1., 0.); lightness = ambientLight + (1. - ambientLight) * lightness; gl_FragColor = vec4(brown * lightness, 1.); }

Смеђи предмет са сунчевом и амбијенталном светлошћу

Исти ефекат можете постићи увођењем класе светлости која чува смер светлости и интензитет амбијенталног светла. Тада можете да промените сенку фрагмената како бисте прилагодили тај додатак.

Сада схадер постаје:

function Light () { this.lightDirection = new Vector3(-1, -1, -1) this.ambientLight = 0.3 } Light.prototype.use = function (shaderProgram) { var dir = this.lightDirection var gl = shaderProgram.gl gl.uniform3f(shaderProgram.lightDirection, dir.x, dir.y, dir.z) gl.uniform1f(shaderProgram.ambientLight, this.ambientLight) }

Тада можете дефинисати светлост:

this.ambientLight = gl.getUniformLocation(program, 'ambientLight') this.lightDirection = gl.getUniformLocation(program, 'lightDirection')

У класу програма схадер додајте потребне униформе:

Renderer.prototype.render = function (camera, light, objects) { this.gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) var shader = this.shader if (!shader) { return } shader.use() light.use(shader) camera.use(shader) objects.forEach(function (mesh) { mesh.draw(shader) }) }

У програму додајте позив новом светлу у приказивачу:

var light = new Light() loop() function loop () { renderer.render(camera, light, objects) requestAnimationFrame(loop) }

Петља ће се тада мало променити:

sampler2D

Ако сте све урадили како треба, приказана слика треба да буде иста као на претходној слици.

Последњи корак који треба размотрити је додавање стварне текстуре нашем моделу. Урадимо то сада.

Додавање текстура

ХТМЛ5 има сјајну подршку за учитавање слика, тако да нема потребе за лудим рашчлањивањем слика. Слике се прослеђују ГЛСЛ-у као sampler2D тако што ће сенчнику рећи коју од повезаних текстура треба узорковати. Постоји ограничен број текстура које се могу повезати, а ограничење се заснива на коришћеном хардверу. А #ifdef GL_ES precision highp float; #endif uniform vec3 lightDirection; uniform float ambientLight; uniform sampler2D diffuse; varying vec3 vNormal; varying vec2 vUv; void main() { float lightness = -clamp(dot(normalize(vNormal), normalize(lightDirection)), -1., 0.); lightness = ambientLight + (1. - ambientLight) * lightness; gl_FragColor = vec4(texture2D(diffuse, vUv).rgb * lightness, 1.); } могу се питати за боје на одређеним позицијама. Овде долазе УВ координате. Ево примера где смо браон замењивали узоркованим бојама.

this.diffuse = gl.getUniformLocation(program, 'diffuse')

Нова униформа мора се додати на листу у програму за сенчење:

function Texture (gl, image) { var texture = gl.createTexture() // Set the newly created texture context as active texture gl.bindTexture(gl.TEXTURE_2D, texture) // Set texture parameters, and pass the image that the texture is based on gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image) // Set filtering methods // Very often shaders will query the texture value between pixels, // and this is instructing how that value shall be calculated gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) this.data = texture this.gl = gl } Texture.prototype.use = function (uniform, binding) binding = Number(binding) Texture.load = function (gl, url) { return new Promise(function (resolve) { var image = new Image() image.onload = function () { resolve(new Texture(gl, image)) } image.src = url }) }

На крају ћемо применити учитавање текстуре. Као што је претходно речено, ХТМЛ5 пружа могућности за учитавање слика. Све што треба да урадимо је да слику пошаљемо на ГПУ:

sampler2D

Процес се не разликује много од процеса који се користи за учитавање и везивање ВБО-а. Главна разлика је у томе што више не везујемо атрибут, већ индекс текстуре вежемо за целобројну униформу. Тхе Mesh типе није ништа друго до померање показивача на текстуру.

Сада све што треба учинити је проширити function Mesh (gl, geometry, texture) { // added texture var vertexCount = geometry.vertexCount() this.positions = new VBO(gl, geometry.positions(), vertexCount) this.normals = new VBO(gl, geometry.normals(), vertexCount) this.uvs = new VBO(gl, geometry.uvs(), vertexCount) this.texture = texture // new this.vertexCount = vertexCount this.position = new Transformation() this.gl = gl } Mesh.prototype.destroy = function () { this.positions.destroy() this.normals.destroy() this.uvs.destroy() } Mesh.prototype.draw = function (shaderProgram) { this.positions.bindToAttribute(shaderProgram.position) this.normals.bindToAttribute(shaderProgram.normal) this.uvs.bindToAttribute(shaderProgram.uv) this.position.sendToGpu(this.gl, shaderProgram.model) this.texture.use(shaderProgram.diffuse, 0) // new this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertexCount) } Mesh.load = function (gl, modelUrl, textureUrl) { // new var geometry = Geometry.loadOBJ(modelUrl) var texture = Texture.load(gl, textureUrl) return Promise.all([geometry, texture]).then(function (params) { return new Mesh(gl, params[0], params[1]) }) } класе, такође за обраду текстура:

var renderer = new Renderer(document.getElementById('webgl-canvas')) renderer.setClearColor(100, 149, 237) var gl = renderer.getContext() var objects = [] Mesh.load(gl, '/assets/sphere.obj', '/assets/diffuse.png') .then(function (mesh) { objects.push(mesh) }) ShaderProgram.load(gl, '/shaders/basic.vert', '/shaders/basic.frag') .then(function (shader) { renderer.setShader(shader) }) var camera = new Camera() camera.setOrthographic(16, 10, 10) var light = new Light() loop() function loop () { renderer.render(camera, light, objects) requestAnimationFrame(loop) }

И последњи главни сценарио изгледао би овако:

function loop () { renderer.render(camera, light, objects) camera.position = camera.position.rotateY(Math.PI / 120) requestAnimationFrame(loop) }

Текстурни објекат са светлосним ефектима

Чак је и анимирање у овом тренутку лако. Ако сте желели да се камера окреће око нашег објекта, то можете учинити додавањем само једног реда кода:

void main() { float lightness = -clamp(dot(normalize(vNormal), normalize(lightDirection)), -1., 0.); lightness = lightness > 0.1 ? 1. : 0.; // new lightness = ambientLight + (1. - ambientLight) * lightness; gl_FragColor = vec4(texture2D(diffuse, vUv).rgb * lightness, 1.); }

Ротирана глава током анимације камере

Слободно се играјте са схадерима. Додавањем једног реда кода претворићете ово реалистично осветљење у нешто цртано.

|_+_|

Једноставно је као рећи осветљењу да уђе у своје крајности на основу тога да ли је прешло задати праг.

Глава са примењеним осветљењем из цртаћа

Где ићи даље

Постоји много извора информација за учење свих трикова и замршености ВебГЛ-а. И најбољи део је тај што ако не можете да пронађете одговор који се односи на ВебГЛ, можете га потражити у ОпенГЛ-у, јер се ВебГЛ у великој мери заснива на подскупу ОпенГЛ-а, а нека имена се мењају.

Без посебног редоследа, ево неколико сјајних извора за детаљније информације, како за ВебГЛ, тако и за ОпенГЛ.

  • Основе ВебГЛ-а
  • Учење ВебГЛ-а
  • Врло детаљно Водич за ОпенГЛ водећи вас кроз све овде описане темељне принципе, на врло спор и детаљан начин.
  • И постоје многи , многи друге веб локације посвећене томе да вас науче принципима рачунарске графике.
  • МДН документација за ВебГЛ
  • Спецификација Кхронос ВебГЛ 1.0 јер ако сте заинтересовани за разумевање техничких детаља како ВебГЛ АПИ треба да функционише у свим рубним случајевима.

Разумевање основа

Шта је ВебГЛ?

ВебГЛ је ЈаваСцрипт АПИ који додаје изворну подршку за приказивање 3Д графике у компатибилним веб прегледачима, путем АПИ-ја сличног ОпенГЛ-у.

Како су 3Д модели представљени у меморији?

Најчешћи начин представљања 3Д модела је низ низ врхова, од којих сваки има дефинисан положај у простору, нормалу површине чији врх треба да буде и координате на текстури која се користи за бојење модела. Ови врхови се затим стављају у групе од по три, да би се формирали троуглови.

Шта је вертек схадер?

Вертек схадер је део цевовода за приказивање који обрађује појединачне врхове. Позив сенку за сенке прима један врх и даје један врх након примене свих могућих трансформација у врх. То омогућава примену кретања и деформација на целе предмете.

Шта је фрагмент схадер?

Схадер фрагмента је део цевовода за приказивање који узима пиксел објекта на екрану, заједно са својствима објекта на тој позицији пиксела, и за њега може да генерише боју, дубину и друге податке.

Како се предмети крећу у 3Д сцени?

Све вертексне позиције објекта су у односу на његов локални координатни систем, који је представљен матрицом идентитета 4к4. Ако померимо тај координатни систем множењем матрицама трансформације, врхови објекта се померају са њим.

Ко, шта и зашто - Водич за методе тестирања корисника

Уи Десигн

Ко, шта и зашто - Водич за методе тестирања корисника
Представник за развој продаје, СМБ

Представник за развој продаје, СМБ

Остало

Популар Постс
Како створити бот за анализу расположења е-поште: Водич за НЛП.
Како створити бот за анализу расположења е-поште: Водич за НЛП.
Поуке из инвестиционе стратегије Варрена Буффетта и његове грешке
Поуке из инвестиционе стратегије Варрена Буффетта и његове грешке
Зашто отплата дељења не успева? Неки предложени лекови
Зашто отплата дељења не успева? Неки предложени лекови
Повећајте своју продуктивност помоћу Амазон Веб Сервицес
Повећајте своју продуктивност помоћу Амазон Веб Сервицес
Развој Андроид ТВ-а - Долазе велики екрани, припремите се!
Развој Андроид ТВ-а - Долазе велики екрани, припремите се!
 
Доступност на мрежи: зашто се стандарди В3Ц често игноришу
Доступност на мрежи: зашто се стандарди В3Ц често игноришу
Алати наредбеног ретка за програмере
Алати наредбеног ретка за програмере
Зен девРант-а
Зен девРант-а
Заступства и гаранција: Алат за спајања и преузимања о коме би сваки продавац требао знати
Заступства и гаранција: Алат за спајања и преузимања о коме би сваки продавац требао знати
Значај дизајна усмереног на човека у дизајну производа
Значај дизајна усмереног на човека у дизајну производа
Популар Постс
  • вештачка интелигенција утиче на економију
  • у развијању изјаве о проблему, истраживач говори о сваком од следећих осим:
  • што од следећег није део плана пројектовања базе података
  • у односу на савете и трикове кода
  • како пронаћи цурење меморије у Јави
  • како пронаћи старе бројеве кредитних картица
  • Ц# водич за јединично тестирање
Категорије
  • Агиле Талент
  • Финансијски Процеси
  • Дизајн Бренда
  • Трендови
  • © 2022 | Сва Права Задржана

    portaldacalheta.pt