Войти
ПрограммированиеФорумГрафика

баг в реализации примитивного освещения. простого шейдера

#0
4:09, 13 июня 2015

Доброго времени суток!
Я никогда не занимался программированием графики. С чудных пор меня стали интересовать алгоритмы которые связаны с графикой. Движение объекта в 3d, вращение и т.п.
Платформу для экспериментов выбрал самую простую - threeJS. Гибкий язык и упрощенная модель работы с данными(автогенерация Faces, расчет нормалей к плоскости и т.п), что избавило меня от простых расчетов.
Ну да ладно, это блабла.

Движение объектов(в том числе с ускорением), вращение и тому подобные плюшки я реализовал. Сейчас меня заинтересовал вопрос написания шейдеров. Я решил реализовать самый примитивный.
В threejs есть изначально самый простой шейдер освещения - phongMaterial, но мне было интересно покопаться.

    var faceIndices = ['a', 'b', 'c', 'd'];  
    var color, r, g, b;

      for(var i=0;i<sphere.faces.length;i++){
        var face = sphere.faces[ i ];   

        // determine if face is a tri or a quad
       // var numberOfSides = ( face instanceof THREE.Face3 ) ? 3 : 4;

        var normale = face.normal;
        var camera_position = {x:3000, y:50, z:0};
        
        var deg = Math.atan2(
          normale.x*camera_position.y-camera_position.x*normale.y,
          normale.x*camera_position.x+normale.y*camera_position.y 
          );

        deg =  deg * 180/Math.PI;

  // предел допустимого угла между вектром освещения и нормалью полигона. все полигоны которые не подпадают под данное условие не обрабатываются
        if(deg<180 && deg>-180){
  
  //по модулю
          deg=Math.abs(deg);

  //значение в процентах от максимально допустимого угла отклонения
          var percentByDeg = ((deg/180)*100);
  
  // значение R канала на 1%
          var valueColorByOnePercent = 255/100;
        // обращаю проценты что бы  освещенная сторона была яркой, а не темной.
   var intensive = (100-percentByDeg)*valueColorByOnePercent;
  
  // степень интенсивности
          var res = intensive/28; 
          r= res;
        } else r = false;

        for( var j = 0; j < 3; j++ )  
        {
            var vertexIndex = face[ faceIndices[ j ] ];
            var color = new THREE.Color();
            if(r) {
    // обновляю значение R канала
              color.setRGB( r, 1,1 );
            } else {
              color.setRGB( 1, 1,1 );
            }
            face.vertexColors[ j ] = color; 
        }
      }

Алгоритм нигде не тырил. Решил придумать сам. К себе пока не строг. Собственно вопрос: почему возникает коллизия?


imglight | баг в реализации примитивного освещения. простого шейдера


Нормально ли это? И, если не затруднит, посоветуйте материал для новчика как реализовать прозрачность. Какие разделы математики надо знать для этого. Я очень хочу научиться)


#1
5:52, 13 июня 2015

должно быть что-то вроде такого

      var faceIndices = ['a', 'b', 'c', 'd'];  
    var color, r, g, b;

      for(var i=0;i<sphere.faces.length;i++){
        var face = sphere.faces[ i ];   

        var normale = face.normal;
        var camera_position = {x:3000, y:50, z:0};
        var light_dir = {x:0, y:-1, z:0};
        //light_dir = -light_dir.normalize();
        
        var cos = light_dir.x*normale.x+light_dir.y*normale.y+light_dir.z*normale.z;

        for( var j = 0; j < 3; j++ )  
        {
            var vertexIndex = face[ faceIndices[ j ] ];
            var color = new THREE.Color();
            if(cos > 0) {
              color.setRGB( 254*cos+1, 1,1 );
            } else {
              color.setRGB( 1, 1,1 );
            }
            face.vertexColors[ j ] = color; 
        }
      }
jb
> Какие разделы математики надо знать для этого
Векторную алгебру и тригонометрию

#2
15:01, 13 июня 2015
     sphere = new THREE.SphereGeometry( 170,  30, 30);
      var color, r, g, b, cos, face;
      var faceIndices = ['a', 'b', 'c', 'd'];  

      for(var i=0; i<sphere.faces.length; i++){
        face = sphere.faces[i];
        var normale = face.normal;


        var xv = 0 - light.position.x,
            xy = 0 - light.position.y,
            xz = 0 - light.position.z;


        var  vector_length =  Math.sqrt(xv*xv + xy*xy + xz*xz);

        var Vlight={
          x:light.position.x/vector_length, 
          y:light.position.y/vector_length, 
          z:light.position.z/vector_length
        };

        cos = Vlight.x*normale.x + Vlight.y*normale.y + Vlight.z*normale.z;
                  
        for(var j=0; j<3; j++){
              var vertexIndex = face[ faceIndices[ j ] ];
              var color = new THREE.Color();
              if(cos) {
                color.setRGB( cos*254, 1,1 );
              } else {
                color.setRGB( 0, 0,0 );
              }
              face.vertexColors[ j ] = color; 
          }
      }

      var obj_material = new THREE.MeshBasicMaterial( 
      { 
        color: 0x100101, 
      //  shading: THREE.FlatShading, 
        vertexColors: THREE.VertexColors 
      } );

Спасибо, Ваш вариант гораздо локаничнее. Но с ним не будет возможным сделать вариант затухания. Точнее говоря я не представляю как это реализовать. Т.е когда объект освещения гораздо больше объекта "приемника" и дистанция не большая - угол допустимого освещения будет больше. После удаления объекта освещение на бОльшую дистанцию угол составит всего 180 градусов. как в случае с косиносом. С маленьким объектом по другому- чем ближе- тем меньше угол освещения, чем дальше- тем больше

#3
15:25, 13 июня 2015

jb
> Нормально ли это?
нет, сделай видео с динамически перемещающимся источником или сферой по всем направлениям, думаю сразу будет видно в чем косяк

#4
15:46, 13 июня 2015

думаю что проблема в этом

        var deg = Math.atan2(
          normale.x*camera_position.y-camera_position.x*normale.y,
          normale.x*camera_position.x+normale.y*camera_position.y 
          );
т.е. для угла не учитывается координата z

#5
22:35, 13 июня 2015

Все, разобрался. Вполне не плохо вышло. Теперь буду работать над имитацией освещения от источников "бОльшего"  и "меньшего" размеров по диаметру нежели приемник. Думаю что здесь стоит брать коэффициент (borderDeg) в отношении дистанции к диаметру объекта-источника. так копаю?
Или нужно проходить по контуру объекта -источника и с каждой вершины проводить вектор на полигоны контура объекта-приемника "сфера" и дальше уже определять границу рассеивания?
Извиняюсь за свой китайский

   sphere = new THREE.SphereGeometry( 150,  250, 250);

      var faceIndices = ['a', 'b', 'c'],
          //Value R channel by  1%  
          percentByDeg,
          //Value as a percentage of the maximum angle of deflection
          valueColorByOnePercent,
          //Glare level
          intensive;

      var r, g, b, cos, 
          normale,
          face, 
          //Maximum deflection angle
          borderDeg = 120;

      for(var i=0;i<sphere.faces.length;i++){
        face =  sphere.faces[i];
        normale = face.normal;

        //stub of time
        var lightVector = {x:100, y:20, z:0};

        //Get vector
        var xv = 0 - lightVector.x,
            xy = 0 - lightVector.y,
            xz = 0 - lightVector.z;

        var  vector_length =  Math.sqrt(xv*xv + xy*xy + xz*xz);

        //Normalize vector
        var Vlight={
          x:lightVector.x/vector_length, 
          y:lightVector.y/vector_length, 
          z:lightVector.z/vector_length
        };

       //   cos =  Vlight.x*normale.x + Vlight.y*normale.y + Vlight.z*normale.z;

        cos= (Vlight.x*normale.x + Vlight.y*normale.y + Vlight.z*normale.z)/
          (
            Math.sqrt(Math.pow( Vlight.x, 2) + Math.pow( Vlight.y, 2) + Math.pow( Vlight.z, 2))  
            *  
            Math.sqrt(Math.pow( normale.x, 2) + Math.pow( normale.y, 2) + Math.pow( normale.z, 2)) 
          );

        //to angle
        deg = Math.acos(cos) * 180/Math.PI;
  

        if(deg<borderDeg && deg>-borderDeg){
          percentByDeg = ((deg/120)*100),
          valueColorByOnePercent = 255/100,
          intensive = (100-percentByDeg)*valueColorByOnePercent;

          r = intensive/17; 
        } else r = false;


        for( var j = 0; j < 3; j++ )  
        {
            var vertexIndex = face[ faceIndices[ j ] ];
            var color = new THREE.Color();
            if(r) {
              color.setRGB( r, 1,1 );
            } else {
              color.setRGB( 0, 0,0 );
            }
            face.vertexColors[ j ] = color; 
        }
      }

Вот пример который показывает освещение сферы не точечным источником света. Пока, конечно же, просто имитация.

sphere | баг в реализации примитивного освещения. простого шейдера

Имитация освещения объектом-источником меньшего размера нежели чем приемник-сфера.
sphere | баг в реализации примитивного освещения. простого шейдера

#6
13:46, 14 июня 2015

Красивый плавный переход

ПрограммированиеФорумГрафика

Тема в архиве.