Beispiel #1
0
  protected void renderMultipassLighting(Shader shader, Geometry g, RenderManager rm) {

    Renderer r = rm.getRenderer();
    LightList lightList = g.getWorldLightList();
    Uniform lightDir = shader.getUniform("g_LightDirection");
    Uniform lightColor = shader.getUniform("g_LightColor");
    Uniform lightPos = shader.getUniform("g_LightPosition");
    Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
    boolean isFirstLight = true;
    boolean isSecondLight = false;

    for (int i = 0; i < lightList.size(); i++) {
      Light l = lightList.get(i);
      if (l instanceof AmbientLight) {
        continue;
      }

      if (isFirstLight) {
        // set ambient color for first light only
        ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
        isFirstLight = false;
        isSecondLight = true;
      } else if (isSecondLight) {
        ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
        // apply additive blending for 2nd and future lights
        r.applyRenderState(additiveLight);
        isSecondLight = false;
      }

      TempVars vars = TempVars.get();
      Quaternion tmpLightDirection = vars.quat1;
      Quaternion tmpLightPosition = vars.quat2;
      ColorRGBA tmpLightColor = vars.color;
      Vector4f tmpVec = vars.vect4f;

      ColorRGBA color = l.getColor();
      tmpLightColor.set(color);
      tmpLightColor.a = l.getType().getId();
      lightColor.setValue(VarType.Vector4, tmpLightColor);

      switch (l.getType()) {
        case Directional:
          DirectionalLight dl = (DirectionalLight) l;
          Vector3f dir = dl.getDirection();

          tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);
          tmpLightDirection.set(0, 0, 0, 0);
          lightDir.setValue(VarType.Vector4, tmpLightDirection);
          break;
        case Point:
          PointLight pl = (PointLight) l;
          Vector3f pos = pl.getPosition();
          float invRadius = pl.getInvRadius();

          tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);
          tmpLightDirection.set(0, 0, 0, 0);
          lightDir.setValue(VarType.Vector4, tmpLightDirection);
          break;
        case Spot:
          SpotLight sl = (SpotLight) l;
          Vector3f pos2 = sl.getPosition();
          Vector3f dir2 = sl.getDirection();
          float invRange = sl.getInvSpotRange();
          float spotAngleCos = sl.getPackedAngleCos();

          tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
          lightPos.setValue(VarType.Vector4, tmpLightPosition);

          // We transform the spot directoin in view space here to save 5 varying later in the
          // lighting shader
          // one vec4 less and a vec4 that becomes a vec3
          // the downside is that spotAngleCos decoding happen now in the frag shader.
          tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0);
          rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
          tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);

          lightDir.setValue(VarType.Vector4, tmpLightDirection);

          break;
        default:
          throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
      }
      vars.release();
      r.setShader(shader);
      r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
    }

    if (isFirstLight && lightList.size() > 0) {
      // There are only ambient lights in the scene. Render
      // a dummy "normal light" so we can see the ambient
      ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
      lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
      lightPos.setValue(VarType.Vector4, nullDirLight);
      r.setShader(shader);
      r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
    }
  }
Beispiel #2
0
  /**
   * Called by {@link RenderManager} to render the geometry by using this material.
   *
   * <p>The material is rendered as follows:
   *
   * <ul>
   *   <li>Determine which technique to use to render the material - either what the user selected
   *       via {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
   *       Material.selectTechnique()}, or the first default technique that the renderer supports
   *       (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering
   *       capabilities})
   *       <ul>
   *         <li>If the technique has been changed since the last frame, then it is notified via
   *             {@link Technique#makeCurrent(com.jme3.asset.AssetManager, boolean,
   *             java.util.EnumSet) Technique.makeCurrent()}. If the technique wants to use a shader
   *             to render the model, it should load it at this part - the shader should have all
   *             the proper defines as declared in the technique definition, including those that
   *             are bound to material parameters. The technique can re-use the shader from the last
   *             frame if no changes to the defines occurred.
   *       </ul>
   *   <li>Set the {@link RenderState} to use for rendering. The render states are applied in this
   *       order (later RenderStates override earlier RenderStates):
   *       <ol>
   *         <li>{@link TechniqueDef#getRenderState() Technique Definition's RenderState} - i.e.
   *             specific renderstate that is required for the shader.
   *         <li>{@link #getAdditionalRenderState() Material Instance Additional RenderState} - i.e.
   *             ad-hoc renderstate set per model
   *         <li>{@link RenderManager#getForcedRenderState() RenderManager's Forced RenderState} -
   *             i.e. renderstate requested by a {@link com.jme3.post.SceneProcessor} or
   *             post-processing filter.
   *       </ol>
   *   <li>If the technique {@link TechniqueDef#isUsingShaders() uses a shader}, then the uniforms
   *       of the shader must be updated.
   *       <ul>
   *         <li>Uniforms bound to material parameters are updated based on the current material
   *             parameter values.
   *         <li>Uniforms bound to world parameters are updated from the RenderManager. Internally
   *             {@link UniformBindingManager} is used for this task.
   *         <li>Uniforms bound to textures will cause the texture to be uploaded as necessary. The
   *             uniform is set to the texture unit where the texture is bound.
   *       </ul>
   *   <li>If the technique uses a shader, the model is then rendered according to the lighting mode
   *       specified on the technique definition.
   *       <ul>
   *         <li>{@link LightMode#SinglePass single pass light mode} fills the shader's light
   *             uniform arrays with the first 4 lights and renders the model once.
   *         <li>{@link LightMode#MultiPass multi pass light mode} light mode renders the model
   *             multiple times, for the first light it is rendered opaque, on subsequent lights it
   *             is rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth
   *             writing disabled.
   *       </ul>
   *   <li>For techniques that do not use shaders, fixed function OpenGL is used to render the model
   *       (see {@link GL1Renderer} interface):
   *       <ul>
   *         <li>OpenGL state ({@link FixedFuncBinding}) that is bound to material parameters is
   *             updated.
   *         <li>The texture set on the material is uploaded and bound. Currently only 1 texture is
   *             supported for fixed function techniques.
   *         <li>If the technique uses lighting, then OpenGL lighting state is updated based on the
   *             light list on the geometry, otherwise OpenGL lighting is disabled.
   *         <li>The mesh is uploaded and rendered.
   *       </ul>
   * </ul>
   *
   * @param geom The geometry to render
   * @param rm The render manager requesting the rendering
   */
  public void render(Geometry geom, RenderManager rm) {
    autoSelectTechnique(rm);

    Renderer r = rm.getRenderer();

    TechniqueDef techDef = technique.getDef();

    if (techDef.getLightMode() == LightMode.MultiPass && geom.getWorldLightList().size() == 0) {
      return;
    }

    if (rm.getForcedRenderState() != null) {
      r.applyRenderState(rm.getForcedRenderState());
    } else {
      if (techDef.getRenderState() != null) {
        r.applyRenderState(
            techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
      } else {
        r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
      }
    }

    // update camera and world matrices
    // NOTE: setWorldTransform should have been called already
    if (techDef.isUsingShaders()) {
      // reset unchanged uniform flag
      clearUniformsSetByCurrent(technique.getShader());
      rm.updateUniformBindings(technique.getWorldBindUniforms());
    }

    // setup textures and uniforms
    for (int i = 0; i < paramValues.size(); i++) {
      MatParam param = paramValues.getValue(i);
      param.apply(r, technique);
    }

    Shader shader = technique.getShader();

    // send lighting information, if needed
    switch (techDef.getLightMode()) {
      case Disable:
        r.setLighting(null);
        break;
      case SinglePass:
        updateLightListUniforms(shader, geom, 4);
        break;
      case FixedPipeline:
        r.setLighting(geom.getWorldLightList());
        break;
      case MultiPass:
        // NOTE: Special case!
        resetUniformsNotSetByCurrent(shader);
        renderMultipassLighting(shader, geom, rm);
        // very important, notice the return statement!
        return;
    }

    // upload and bind shader
    if (techDef.isUsingShaders()) {
      // any unset uniforms will be set to 0
      resetUniformsNotSetByCurrent(shader);
      r.setShader(shader);
    }

    r.renderMesh(geom.getMesh(), geom.getLodLevel(), 1);
  }