Возможно, это лучше перенести в «Я пиарюсь» или «JavaScript» (принимаются предложения). А может, стоит оставить всё как есть. Тем не менее...
3D с z-buffer-ом, субпиксельной точностью и освещением по Гуро на javascript? Да кто угодно сможет это сделать, используя canvas!
Можно долго и вкусно описывать преимущества канваса, но статья не про это; не менее интересно
посмотреть, чем же канвас плох.
Яненормальный псих завёл блог, в котором собираюсь регулярно писать о создании различных эффектов и игр на javascript, не использующих канвас:
Для начала, необходимо позаботится о пользователях IE, предложив им поддержку тэга canvas в виде Chrome Frame: прописываем meta, подключаем гуглоскрипт, создаём блок no-canvas и правим стили для гугловского iframe (по умолчанию он появляется в центре страницы)
Функция main (init вызывается по таймауту, потому что иначе хром (dev версия) иногда не прорисовывает background у body до конца):
Всю отрисовку будем вести в буфере 128x128, а потом его выводить с 3-х кратным увеличением (используя putImageData).
Представим фигуру в виде объекта и напишем читерскую функцию box, которая будет создавать кубы сразу с нормалями:
Выполним всю чёрную работу — повернём объект (вместе с нормалями — это быстрее, чем считать нормали заново) и спроецируем его в 2D (мне было лень нормально считать уровень освещённости, по-этому я использовал метод научного тыкаtm для нахождения магической формулы и магических коеффициентов 0.7 и 3)
Напишем процедуру рисования горизонтальных линий (не самую оптимальную) и треугольников (тоже не чемпион по скорости):
Остаётся написать функцию loop и запустить её через setInterval.
Полный текст скрипта доступен по адресу http://zame-dev.org/projects/nocanvas/0000/index.html (просто загляните в исходный код страницы)
Читай блог @ подписывайся на RSS
3D с z-buffer-ом, субпиксельной точностью и освещением по Гуро на javascript? Да кто угодно сможет это сделать, используя canvas!
Можно долго и вкусно описывать преимущества канваса, но статья не про это; не менее интересно
посмотреть, чем же канвас плох.
Так чем же?
- Он ни в какую не работает в Обозревателе Интернета одной большой известной корпорации: эмулируя канвас через VML мы отрезаем самые вкусные пиксельные манипуцляции, а эмуяция пиксельных вкусняшек на flash безбожно тормозит из-за того, что где-то в недрах ExternalInterface разработчики из Adobe вставили пару функций sleep :)
- Только Google Chrome (aka Chromium) может обеспечить достаточную скорость исполнения джаваскрипта. Во всех остальных браузерах вкусный и сексуальный эффект рискует превратиться в слайд-шоу.
- И, наконец, самое главное — это неспортивно! Я уверен, что пока есть люди пишущие сапёров на bat файлах, тетрисы на sed и боярские диалекты C++, программирование ради самого процесса программирования будет интересовать массы :)
Я
- раз в две недели я буду писать о каком-либо новом эффекте;
- раз в два месяца я буду рассказывать делать playable demo какой-нибудь игры (как водится, не использующую canvas);
- и, наконец, раз в полгода я буду делать по игре (ну, по крайней мере, я буду очень стараться не сорвать сроки)
Небольшая еретическая статья на затравку — как сделать 3D + z-buffer + subpixel + gouraud shading используя канвас
Шаг 0
Для начала, необходимо позаботится о пользователях IE, предложив им поддержку тэга canvas в виде Chrome Frame: прописываем meta, подключаем гуглоскрипт, создаём блок no-canvas и правим стили для гугловского iframe (по умолчанию он появляется в центре страницы)
<head><br> <meta http-equiv="X-UA-Compatible" content="chrome=1" /><br> <style type="text/css" media="screen"><br>.chrome-install {<br> position: relative;<br> margin: 0;<br> padding: 0;<br> top: 0;<br> left: 0;<br>}<br> </style><br> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script><br></head><br><body onload="main()"><br> <div id="no-canvas" style="display:none;"><br> <h2><canvas> support required.</h2><br> <div id="chrome-install-placeholder"></div><br> </div><br> <div id="canvas-enabled"><br> <canvas id="canvas" width="384" height="384"></canvas><br> </div><br></body><br><br>* This source code was highlighted with Source Code Highlighter.
Функция main (init вызывается по таймауту, потому что иначе хром (dev версия) иногда не прорисовывает background у body до конца):
function main()<br>{<br> canvas = document.getElementById('canvas');<br><br> if (typeof(canvas.getContext) == 'function')<br> {<br> ctx = canvas.getContext('2d');<br> setTimeout(init, 100);<br> }<br> else<br> {<br> document.getElementById('canvas-enabled').style.display = 'none';<br> document.getElementById('no-canvas').style.display = '';<br><br> CFInstall.check({<br> node: 'chrome-install-placeholder',<br> className: 'chrome-install',<br> destination: window.location.href<br> });<br> }<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Шаг 1
Всю отрисовку будем вести в буфере 128x128, а потом его выводить с 3-х кратным увеличением (используя putImageData).
var scr = [];<br>var zbuf = [];<br>var WDT = 128;<br>var HGT = 128;<br><br>function blit()<br>{<br> var cd = ctx.createImageData(canvas.width, canvas.height);<br> var data = cd.data;<br> var ind = 0;<br><br> for (var y = 0; y < HGT; y++)<br> {<br> var line = scr[y];<br><br> for (var x = 0; x < WDT; x++)<br> {<br> data[ind++] = line[x][0]; data[ind++] = line[x][1]; data[ind++] = line[x][2]; data[ind++] = 255;<br>....<br> }<br><br> ctx.putImageData(cd, 0, 0);<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Шаг 2
Представим фигуру в виде объекта и напишем читерскую функцию box, которая будет создавать кубы сразу с нормалями:
{<br> points: [<br> {x: point_x, y: point_y, z: point_z, n: { x: point_normal_x, y: point_normal_y, z: point_normal_z } },<br> ....<br> ],<br> faces: [<br> [point_1, point_2, point_3, { x: face_normal_x, y: face_normal_y, z: face_normal_z }],<br> ....<br> ],<br> color: object_color,<br> rot: [rot_x, rot_y, rot_z]<br>}<br><br>function box(size, cl, rot)<br>{<br> var norm = 1 / Math.sqrt(3);<br><br> return {<br> points: [<br> { x: -size, y: size, z: -size, n: { x: -norm, y: norm, z: -norm } },<br> ....<br> ],<br> faces: [<br> [ 0, 1, 2, { x: 0, y: 1, z: 0 } ],<br> ....<br> ],<br> color: cl,<br> rot: rot<br> };<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Шаг 3
Выполним всю чёрную работу — повернём объект (вместе с нормалями — это быстрее, чем считать нормали заново) и спроецируем его в 2D (мне было лень нормально считать уровень освещённости, по-этому я использовал метод научного тыкаtm для нахождения магической формулы и магических коеффициентов 0.7 и 3)
function project(pt, rm)<br>{<br> var rot = {<br> x: (pt.x*rm.oxx + pt.y*rm.oxy + pt.z*rm.oxz),<br> y: (pt.x*rm.oyx + pt.y*rm.oyy + pt.z*rm.oyz),<br> z: (pt.x*rm.ozx + pt.y*rm.ozy + pt.z*rm.ozz + ZPOS)<br> };<br><br> var l = 1 - (Math.cos(pt.n.x*rm.ozx + pt.n.y*rm.ozy + pt.n.z*rm.ozz) - 0.7) * 3;<br> l = Math.max(0, Math.min(1, l));<br><br> return {<br> x: ((WDT / 2) + rot.x * (WDT / 2 - 1) / rot.z),<br> y: ((HGT / 2) + rot.y * (HGT / 2 - 1) / rot.z),<br> z: rot.z,<br> l: l<br> };<br>}<br><br>function draw_object(obj)<br>{<br> var ax = (tm * obj.rot[0]);<br> var ay = (tm * obj.rot[1]);<br> var az = (tm * obj.rot[2]);<br><br> var s1 = Math.sin(ax); var s2 = Math.sin(ay); var s3 = Math.sin(az);<br> var c1 = Math.cos(ax); var c2 = Math.cos(ay); var c3 = Math.cos(az);<br><br> var rm = {<br> oxx: (c2 * c1),<br> oxy: (c2 * s1),<br> ....<br> };<br><br> var pr = [];<br><br> for (var i = 0; i < obj.points.length; i++) {<br> pr.push(project(obj.points[i], rm));<br> }<br><br> for (var i = 0; i < obj.faces.length; i++)<br> {<br> var face = obj.faces[i];<br> var fz = (face[3].x*rm.ozx + face[3].y*rm.ozy + face[3].z*rm.ozz);<br><br> if (fz <= 0) {<br> triangle(pr[face[0]], pr[face[1]], pr[face[2]], obj.color);<br> }<br> }<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Шаг 4
Напишем процедуру рисования горизонтальных линий (не самую оптимальную) и треугольников (тоже не чемпион по скорости):
function hline(y, xl, xr, cl, ll, lr, zl, zr)<br>{<br> // Растянем линию горизонтально - так как мы используем треугольники,<br> // а не честные полигоны, то при субпиксельной отрисовке без этого хака<br> // иногда проявляются артефакты<br> xl -= 0.5;<br> xr += 0.5;<br> ....<br>}<br><br>// **в реальном коде используется изменённая функция**<br>function triangle(a, b, c, cl)<br>{<br> .... сортировка вершин ....<br><br> var dxl = (c.x - a.x) / (c.y - a.y);<br> var dxr = (b.x - a.x) / (b.y - a.y);<br> ....<br> var y = a.y;<br><br> while (y < b.y)<br> {<br> // (y - a.y) - не обязательно целочисленное<br> // за счёт этого достигается субпиксельная точность<br> xl = sx + dxl * (y - a.y);<br> xr = sx + dxr * (y - a.y);<br> ....<br> hline(y, xl, xr, cl, ll, lr, zl, zr);<br> y++;<br> }<br><br> ....<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Шаг 5
Остаётся написать функцию loop и запустить её через setInterval.
function loop()<br>{<br> tm = ((new Date()).valueOf() - st) / 1.5;<br><br> draw_scene();<br> blit();<br>}<br><br>* This source code was highlighted with Source Code Highlighter.
Эпилог
Полный текст скрипта доступен по адресу http://zame-dev.org/projects/nocanvas/0000/index.html (просто загляните в исходный код страницы)
Читай блог @ подписывайся на RSS