最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - WebGL fragment-shader for multiple light sources? - Stack Overflow

programmeradmin1浏览0评论

I want to be able to append multiple light sources to each node of my scene graph, but I have no clue how to do it!

From the tutorials on learningwebgl I learned to either use directional or position lighting, but I couldn't find a good explanation of how to implement multiple light sources.

So, the objective should be, to have the option to append an arbitrary number of light sources to each node, which type can be either directional or position lighting, and if possible and advisable, this should be realized by using only one shader-program (if this isn't the only possibility to do it anyway), because I automatically create the program for each node depending on their specific needs (unless there is already a program on the stack with equal settings).

Based on the tutorials on learningwebgl, my fragment-shader source for a node object using lighting without preset binding to one of the lighting-types could look like this...

precision highp float;

uniform bool uUsePositionLighting;
uniform bool uUseDirectionalLighting;

uniform vec3 uLightPosition;
uniform vec3 uLightDirection;

uniform vec3 uAmbientColor;
uniform vec3 uDirectionalColor;

uniform float uAlpha;

varying vec4 vPosition;
varying vec3 vTransformedNormal;
varying vec3 vColor;


void main (void) {

  float directionalLightWeighting;

  if (uUseDirectionalLighting) {

    directionalLightWeighting = max(dot(vTransformedNormal, uLightDirection), 0.0);

  else if (uUsePositionLighting) {

    vec3 lightDirection = normalize(uLightPosition, vPosition.xyz);

    directionalLightWeighting = max(dot(normalize(vTransformedNormal, lightDirection), 0.0);

  }

  vec3 lightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;

  gl_FragColor = vec4(vColor * lightWeighting, uAlpha);

}

...so, that's basically my poor state of knowledge concerning this subject.

I also ask myself, how adding more light sources would affect the lighting-colors:

I mean, do uAmbientColor and uDirectionalColor have to sum up to 1.0? In this case (and particularly when using more than one light source) it surely would be good to precalculate these values before passing them to the shader, wouldn't it?

I want to be able to append multiple light sources to each node of my scene graph, but I have no clue how to do it!

From the tutorials on learningwebgl. I learned to either use directional or position lighting, but I couldn't find a good explanation of how to implement multiple light sources.

So, the objective should be, to have the option to append an arbitrary number of light sources to each node, which type can be either directional or position lighting, and if possible and advisable, this should be realized by using only one shader-program (if this isn't the only possibility to do it anyway), because I automatically create the program for each node depending on their specific needs (unless there is already a program on the stack with equal settings).

Based on the tutorials on learningwebgl., my fragment-shader source for a node object using lighting without preset binding to one of the lighting-types could look like this...

precision highp float;

uniform bool uUsePositionLighting;
uniform bool uUseDirectionalLighting;

uniform vec3 uLightPosition;
uniform vec3 uLightDirection;

uniform vec3 uAmbientColor;
uniform vec3 uDirectionalColor;

uniform float uAlpha;

varying vec4 vPosition;
varying vec3 vTransformedNormal;
varying vec3 vColor;


void main (void) {

  float directionalLightWeighting;

  if (uUseDirectionalLighting) {

    directionalLightWeighting = max(dot(vTransformedNormal, uLightDirection), 0.0);

  else if (uUsePositionLighting) {

    vec3 lightDirection = normalize(uLightPosition, vPosition.xyz);

    directionalLightWeighting = max(dot(normalize(vTransformedNormal, lightDirection), 0.0);

  }

  vec3 lightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;

  gl_FragColor = vec4(vColor * lightWeighting, uAlpha);

}

...so, that's basically my poor state of knowledge concerning this subject.

I also ask myself, how adding more light sources would affect the lighting-colors:

I mean, do uAmbientColor and uDirectionalColor have to sum up to 1.0? In this case (and particularly when using more than one light source) it surely would be good to precalculate these values before passing them to the shader, wouldn't it?

Share Improve this question asked Jun 2, 2015 at 11:15 user4512363user4512363
Add a ment  | 

2 Answers 2

Reset to default 6

Put your lights into an array and loop over them for each fragment. Start with a fixed array of light sources, unbounded arrays are not supported until OpenGL 4.3 and are more plicated to work with.

Something along the lines of:

uniform vec3 uLightPosition[16];
uniform vec3 uLightColor[16];
uniform vec3 uLightDirection[16];
uniform bool uLightIsDirectional[16];

 ....

 void main(void) {
   vec3 reflectedLightColor;

   // Calculate ining light for all light sources
   for(int i = 0; i < 16; i++) {
     vec3 lightDirection = normalize(uLightPosition[i], vPosition.xyz);
     if (lightIsDirectional[i]) {
       reflectedLightColor += max(dot(vTransformedNormal, uLightDirection[i]), 0.0) * uLightColor[i];
     }
     else  {
       reflectedLightColor += max(dot(normalize(vTransformedNormal, lightDirection), 0.0) * uLightColor[i];
     }
   }

   glFragColor = vec4(uAmbientColor + reflectedLightColor * vColor, uAlpha);
 }

Then you can enable/disable the light sources by setting uLightColor to (0,0,0) for the entries you don't use.

Ambient and directional don't have to sum up to 1, actually the light source can have a strength much stronger than 1.0, but then you will need to do tonemapping to get back to a range of values that can be displayed on a screen, I would suggest playing around to get a feel for what is happening (e.g. what happens when a light source have negative colors, or colors above 1.0?).

uAmbientColor is just a (poor) way to simulate light that has bounced several times in the scene. Otherwise things in shadow bees pletely black Which looks unrealistic.

Reflectance should typically be between 0 and 1 (in this example it would be the parts returned by the 'max' putations), otherwise a lightsource will get stronger when looked at via the material.

@ErikMan's answer is great, but may involve a lot of extra work on the part of the GPU since you're checking every light per fragment, which isn't strictly necessary.

Rather than an array, I'd suggest building a clip-space quadtree. (You can do this in a pute shader if it's supported by your target platform / GL version.)

A node might have a structure such as (pseudocode as my JS is rusty):

typedef struct
{
    uint LightMask; /// bitmask - each light has a bit indicating whether it is active for this node. uint will allow for 32 lights. 

    bool IsLeaf;

} Node;

const uint maxLevels = 4;
const uint maxLeafCount = pow(4,maxLevels);      
const uint maxNodeCount = (4 * maLeafCount - 1) / 3;

/// linear quadtree - node offset = 4 * parentIndex + childIndex;
Node tree[maxNodeCount];

When building the tree, just check each light's clip-space bounding box against the implicit node bounds. (Root goes from (-1,-1) to (+1,+1). Each child is half that size on each dimension. So, you don't really need to store node bounds.)

If the light touches the node, set a bit in Node.LightMask corresponding to the light. If the light pletely contains the node, stop recursing. If it intersects the node, subdivide and continue.

In your fragment shader, find which leaf node contains your fragment, and apply all lights whose bit is set in the leaf node's mask.

You could also store your tree in a mipmap pyramid if you expect it to be dense.

Keep your tiles to a size that is a multiple of 32, preferably square.

vec2 minNodeSize = vec2(2.f / 32);

Now, if you have a small number of lights, this may be overkill. You would probably have to have a lot of lights to see any real performance benefit. Also, a normal loop may help reduce data divergence in your shader, and makes it easier to eliminate branching.

This is one way to implement a simple tiled renderer, and opens the door to having hundreds of lights.

发布评论

评论列表(0)

  1. 暂无评论