2
votes

Shaders: Comment dessiner des points verts 3D sans générer de géométrie?

J'ai une scène Webgl 3D. J'utilise Regl http://regl.party/ . Qui est WebGL. Donc j'écris essentiellement du GLSL.

Ceci est un projet de jeu. J'ai un tableau de positions 3D [[x, y, z] ...] qui sont des balles ou des projectiles. Je veux dessiner ces puces sous la forme d'un simple cube, sphère ou particule. Aucune exigence sur l'apparence.

Comment puis-je créer des shaders et un appel de dessin pour cela sans avoir à créer un jeu de géométrie en double répété pour les puces?

Préférer une réponse avec un exemple de shader vert et frag qui démontre l'entrée de données attendue et peut être rétro-ingénierie pour gérer la couche de liaison du processeur


3 commentaires

Je pense que vous pouvez utiliser un shader de géométrie pour ce faire, mais ne l'ayant pas fait avant, je ne suis pas sûr des détails les plus fins


@AdaRaider geometry shaders non disponibles dans webgl mais bonne suggestion.


Ah oui, j'ai raté cette balise!


4 Réponses :


2
votes

Vous pouvez dessiner toutes les puces sous forme de sprites ponctuels, auquel cas il vous suffit de fournir la position et la taille de chaque puce et de les dessiner sous la forme GL_POINTS . Chaque «point» est pixellisé en un carré en fonction de la sortie de votre vertex shader (qui s'exécute une fois par point). Votre shader de fragment est appelé pour chaque fragment de ce carré et peut colorer le fragment comme il le souhaite - avec une couleur plate, en échantillonnant une texture ou comme vous le souhaitez.

Ou vous pouvez fournir un modèle unique pour toutes les puces, une transformation distincte pour chaque puce, et les dessiner comme GL_TRIANGLES ou GL_TRIANGLE_STRIP instanciés ou autre. Découvrez l'instanciation sur le wiki OpenGL .


1 commentaires

Pouvez-vous développer la suggestion de sprite de particules? cela ne nécessite-t-il pas plus de données pour les faire apparaître plus que de simples pixels? a-t-il besoin d'une texture?



2
votes

Pas un codeur WebGL donc lu avec préjugé ...

  1. Encoder les sommets dans une texture

    méfiez-vous du serrage, utilisez un format de texture qui ne serre pas sur <0.0,+1.0> comme GL_LUMINANCE32F_ARB ou utilisez uniquement des sommets dans cette plage. Pour vérifier l'utilisation du serrage:

  2. Rendre un rectangle unique couvrant tout l'écran

    et utilisez la texture de # 1 comme entrée. Cela garantira qu'un shader de fragment est appelé pour chaque pixel de l'écran / vue exactement une fois.

  3. Le shader de fragment intérieur lit la texture et vérifie la distance entre un fragment et vos sommets

    basé sur celui-ci, rend votre truc ou dicard () fragment ... les sphères sont faciles, mais les boîtes et autres formes peuvent être compliquées à rendre en fonction de la distance du sommet, surtout si elles peuvent être arbitraires orienté (qui nécessitent des informations supplémentaires dans la texture d'entrée).

    Pour faciliter cela, vous pouvez les pré-rendre dans une certaine texture et utiliser la distance comme coordonnées de texture ...

Ma réponse utilise cette technique:


0 commentaires

2
votes

Vous pouvez parfois vous en sortir en utilisant GL_POINTS avec un grand gl_PointSize et un shader de fragment personnalisé. Un exemple illustré ici utilisant la distance au centre du point pour le fragment alpha. (Vous pouvez également tout aussi bien échantillonner une texture)

La prise en charge des grandes tailles de points peut cependant être limitée, alors vérifiez cela avant de décider de cette route.

<!doctype html>
<html>
   <body>
      <canvas width = "400" height = "400" id = "cvs"></canvas>
   </body>
</html>
var canvas = document.getElementById('cvs');
gl = canvas.getContext('webgl'); 

var vertices = [
  -0.5, 0.75,0.0,
   0.0, 0.5, 0.0,
  -0.75,0.25,0.0, 
];

var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

var vertCode =
  `attribute vec3 coord;
   void main(void) {
     gl_Position = vec4(coord, 1.0);
     gl_PointSize = 50.0;
   }`;

var vertShader = gl.createShader(gl.VERTEX_SHADER);  
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);

var fragCode =
  `void main(void) {
     mediump float ds = distance(gl_PointCoord.xy, vec2(0.5,0.5))*2.0;
     mediump vec4 fg_color=vec4(0.0, 0.0, 0.0,1.0- ds);     
     gl_FragColor = fg_color;
  }`;
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);

var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader); 
gl.attachShader(shaderProgram, fragShader);


gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);

gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);

var coord = gl.getAttribLocation(shaderProgram, "coord");

gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coord);

gl.viewport(0,0,canvas.width,canvas.height);
gl.drawArrays(gl.POINTS, 0, 3);


0 commentaires

3
votes

Vous créez une commande regl qui encapsule un tas de données. Vous pouvez alors l'appeler avec un objet.

Chaque uniforme peut prendre une fonction optionnelle pour fournir sa valeur. Cette fonction reçoit un contexte regl comme premier argument, puis l'objet que vous avez passé comme second argument afin que vous puissiez l'appeler plusieurs fois avec un objet différent pour dessiner la même chose (mêmes sommets, même shader) ailleurs. P >

<script src="https://cdnjs.cloudflare.com/ajax/libs/regl/1.3.11/regl.min.js"></script>
var regl = createREGL()

const objects = [];
const numObjects = 100;
for (let i = 0; i < numObjects; ++i) {
  objects.push({
    x: rand(-1, 1),
    y: rand(-1, 1),
    speed: rand(.5, 1.5),
    direction: rand(0, Math.PI * 2),
    color: [rand(0, 1), rand(0, 1), rand(0, 1), 1],
  });
}

function rand(min, max) {
  return Math.random() * (max - min) + min;
}

const starPositions = [[0, 0, 0]];
const starElements = [];
const numPoints = 5;
for (let i = 0; i < numPoints; ++i) {
  for (let j = 0; j < 2; ++j) {
    const a = (i * 2 + j) / (numPoints * 2) * Math.PI * 2;
    const r = 0.5 + j * 0.5;
    starPositions.push([
      Math.sin(a) * r,
      Math.cos(a) * r,
      0,
    ]);
  }
  starElements.push([
    0, 1 + i * 2, 1 + i * 2 + 1,
  ]);
}

const drawStar = regl({
  frag: `
  precision mediump float;
  uniform vec4 color;
  void main () {
    gl_FragColor = color;
  }`,
  vert: `
  precision mediump float;
  attribute vec3 position;
  uniform mat4 mat;
  void main() {
    gl_Position = mat * vec4(position, 1);
  }`,
  attributes: {
    position: starPositions,
  },
  elements: starElements,
  uniforms: {
    mat: (ctx, props) => {
      const {viewportWidth, viewportHeight} = ctx;
      const {x, y} = props;
      const aspect = viewportWidth / viewportHeight;
      return [.1 / aspect, 0, 0, 0,
              0, .1, 0, 0,
              0, 0, 0, 0,
              x, y, 0, 1];
    },
    color: (ctx, props) => props.color,
  }
})

regl.frame(function () {
  regl.clear({
    color: [0, 0, 0, 1]
  });
  objects.forEach((o) => {
    o.direction += rand(-0.1, 0.1);
    o.x += Math.cos(o.direction) * o.speed * 0.01;
    o.y += Math.sin(o.direction) * o.speed * 0.01;
    o.x  = (o.x + 3) % 2 - 1;
    o.y  = (o.y + 3) % 2 - 1;
    drawStar(o);
  });
})


0 commentaires