No Canvas

    Возможно, это лучше перенести в «Я пиарюсь» или «JavaScript» (принимаются предложения). А может, стоит оставить всё как есть. Тем не менее...

    3D с z-buffer-ом, субпиксельной точностью и освещением по Гуро на javascript? Да кто угодно сможет это сделать, используя canvas!

    Можно долго и вкусно описывать преимущества канваса, но статья не про это; не менее интересно
    посмотреть, чем же канвас плох.


    Так чем же?


    • Он ни в какую не работает в Обозревателе Интернета одной большой известной корпорации: эмулируя канвас через VML мы отрезаем самые вкусные пиксельные манипуцляции, а эмуяция пиксельных вкусняшек на flash безбожно тормозит из-за того, что где-то в недрах ExternalInterface разработчики из Adobe вставили пару функций sleep :)
    • Только Google Chrome (aka Chromium) может обеспечить достаточную скорость исполнения джаваскрипта. Во всех остальных браузерах вкусный и сексуальный эффект рискует превратиться в слайд-шоу.
    • И, наконец, самое главное — это неспортивно! Я уверен, что пока есть люди пишущие сапёров на bat файлах, тетрисы на sed и боярские диалекты C++, программирование ради самого процесса программирования будет интересовать массы :)

    Я ненормальный псих завёл блог, в котором собираюсь регулярно писать о создании различных эффектов и игр на javascript, не использующих канвас:
    • раз в две недели я буду писать о каком-либо новом эффекте;
    • раз в два месяца я буду рассказывать делать 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>&lt;canvas&gt; 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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 42

      0
      Неплохая затея, но браузер вешает ужасно, особенно FF
        +3
        В FF3.6 более менее нормально
          0
          В основном на пользователя ориентирована разработка, а разом всем внедрить новые версии не получится явно =)
            0
            А вот и три человека, которым интересна тема no canvas. Слава, если будет еще один — с меня проспоренное пиво.
              +4
              Мне тоже интересна эта тема. Отдайте Славе проспоренное пиво.
                +5
                (голосом робота Бендера) damn!
            0
            В SeaMonkey 2.0.2 тоже значительные тормоза (мешает набирать текст), даже если вкладка с анимацией не активна.
            +2
            Safari 4.0.4 — полет нормальный
              +2
              Ждем «труъ nocanvas».
                +3
                все новые посты будут труъ :)
                +1
                Знаете, все очень интересно звучит, но все-таки я не понимаю, зачем избегать холста. Не работает в ИЕ — это понятно, но изредка бывают ситуации, когда он не нужен.
                Мне видится интерес в первую очередь именно со спортивной точки зрения, а практическое применение пока кажется неоправданным.
                Но как спорт — да, думаю будет интересно. Жду обновлений блога :-)
                  +10
                  мне кажется что на практике сложные эффекты лучше делать на flash.
                  а nocanvas это именно спорт. (а кроме того я до сих пишу на zx-spectrum :)
                    +3
                    А можно попросить написать обзор как раз того, что вы выделил серым цветом? Текущее положение дел с софтом, железом, демосцена и другие «вкусности». Если есть что написать, конечно.
                      +2
                      по крайней статьи на тему zx демосцены на хабре появляются,
                      но если хабрапользователям мало :), то могу попробовать на следующей неделе.
                        +1
                        Спасибо, буду ждать.
                      0
                      Flash не везде есть и это хорошо. Хотя, бы потому, что флешь закрыт и в моей ОС вместо русских букв кракозябры. На n800 тоже квадраты вместо Flash. Если Адоб его откроет, а этого никогда не будет, пусть будет. А остаться без интернета не хочется
                  • UFO just landed and posted this here
                      0
                      субпиксельная точность != антиалиасинг
                      к тому же, в классической бесканвасной реализации нет z-buffer-а
                        –1
                        Воспользовавшись ms paint и zoom было замечено, что нет там никакой субпиксельной точности. Гражданин, вы бредите.

                        Субпиксельный рендеринг — это учет местоположения компонент цвета на экране, чтобы какие-нибудь желто-циановые границы не содержали в себе зеленой полосы. Хороший пример — ClearFont рендеринг в Windows, с его потекшими оранжево-синими краями.
                          –1
                          тов. uppn, если вы действительно гуру 3D, то я бы с удовольствием послушал что такое субпиксельная точность.
                          а то что я знаю про субпиксельную точность на русском хорошо изложено тут -> www.enlight.ru/faq3d/articles/71.htm
                            0
                            en.wikipedia.org/wiki/Subpixel_rendering

                            Ваш Кэп. То есть гуру 3D.
                              0
                              я вам про фому, вы мне про ерёму.
                              ещё раз говорю, что subpixel accuracy != subpixel rendering.
                              если русским ресурсам не верите, почитайте зарубежные — www.antigrain.com/doc/introduction/introduction.agdoc.html
                              (там как раз есть про разницу между субпиксельной точностью и тем что вы хотите (анти-алиасингом))
                              0
                              А за ссылочку спасибо, я об этом ресурсе демокодинга не знал.
                            0
                            Вот если бы вы реализовали z-buffer в классической реализации, вам бы можно было ставить памятник.
                              0
                              будет в следующих статьях (через месяц-два)
                          +2
                          В опере 9.60 не работает :(
                          В хроме норально, но как то убогенько :) но если также будет без canvas'a будет круто. Каким образом реализовывать будете(хотя бы в общих чертах)?
                          Производительность canvas'a для простых 3д объектовработает нормально, опера, фф, сафари, хром. Ие конечно это да :(
                          Но он все равно под линуксом не пашет :)
                          Кстати понравился пример с текстурированием от Nihilogic
                          www.nihilogic.dk/labs/canvas3dtexture_0.2/

                          Пробовал это как то адаптировать чуть чуть под себя(модельку другую сделать, текстурки наложить побольше, поддержку колесика мышинного прикрутить), но строится модель уж больно долго, хотя работает достаточно шустро. (мышинное колесо так и не запустилось в фф, так что там используются кнопки + и — , надо бы поправить но пока это так for fun и руки никак не дойдут.)
                          Если кому интересно дам линк где можно глянуть результат (хотя смотреть особо нечего).

                          Ждем примера 3д без канваса.
                            0
                            А если отдавать построение модели new Worker('worker.js')?
                              0
                              Заюзать веб workers? интересно, но вроде как опера тут же идет лесом, так как я ее юзаю несколько обидно. Но идея интересная.
                            +1
                            А ещё бы счётчик fps бы :) на глаз конечно видно что хром гораздо пошустрее но с циферками интереснее было бы мне кажется
                              0
                              спасибо за статью! Успехов Вам с блогом!
                                +2
                                осторожно, трафик
                                borisov-gleb.googlecode.com/svn/trunk/index.html

                                видеоплеер на canvas (пока grayscale и без стриминга)
                                  +2
                                  [offtop]первый раз в жизни ощутил всю важность этой маленькой фразки «осторожно, трафик» т.к. сижу с gprs'a. Спасибо.[/offtop]
                                  +1
                                  Под Opera 10.50 не работает, валятся ошибки.
                                    0
                                    У меня Dev Chrome, отображается не плохо. Только вот самого накрыло, сижу пырюсь в эти кубики и не могу оторваться.
                                      0
                                      Напишите минимальные требования к браузеру, а то перебробовал весь зоопарк, чтобы запустить (не работает в ff 3.0, opera 10.10 и 10.50)…
                                        0
                                        IE6+, Fx3.5+, Chrome3+, Safari4+
                                          +1
                                          Кстати если заменить var cd=ctx.createImageData(canvas.width, canvas.height); на var cd = ctx.getImageData(0,0,canvas.width, canvas.height);
                                          то работать будет и в опере.
                                            0
                                            спасибо, сделал фикс для оперы. (в опере нет createImageData O_o?)
                                    • UFO just landed and posted this here
                                        0
                                        понравилось
                                        буду следить за вашим блогом
                                          0
                                          Пару дней назад сделал релиз «эмулятора» Canvas на флеш для IE с поддержкой get/put/createImageData(). Смотрите подробности в моей статье:
                                          buzzilo.habrahabr.ru/blog/91130/

                                          Only users with full accounts can post comments. Log in, please.