3
\$\begingroup\$

First off, this is a Uni assignment, but the lecturer is stumped too.

I am trying to imitate a heat lamp. The goal is to mimic what a thermal camera might see while looking at the sphere which is under the influence of the heat lamp.

To do this I need to get information about the heat values of the vertices/fragments from the previous frame, I know that OpenGL doesn't provide a way to do this so I need to 'export' the values back to C++ so that C++ can forward them to the next frame. How do I do that? I was thinking with a texture but ran into some problems, likely related to the ones mentioned in another issue I've been having with the code.

Here is the fragment shader:

const float     lightWeighting = 0.75;
const float     textureWeighting = 0.25;


const float     PI = 3.14;
const float     lightIntensity = 1.0;
const float     constFudge = 0.025;
const float     linearFudge = 0.025;
const float     quadraticFudge = 0.05;

float rend  = 250.0;
float rstart = 50.0;


uniform float       Intensity;
uniform sampler2D   grabTexture;
//varying   sampler2D   HeatValues

varying vec3        Normal;
varying vec3        Vertex;
varying vec2        texCoord;

void main(void)
{   
  vec3 pigPos = Vertex;
  vec3 normPigPos = normalize(pigPos);


  vec3 lightPos = vec3(gl_LightSource[0].position.xyz);

  vec3 normLightPos = normalize(lightPos);




  float effectiveIntesity;


  vec3 normNormal = normalize(vec3(Normal.xyz));

  vec3 t = vec3(pigPos - lightPos);

  float distToLight = length(t);

  float d = dot(normNormal.xyz, normLightPos.xyz);
  if(d > 0.0)//facing the light
  {
    float falloff;

    if( distToLight < rstart )
      falloff = 1.0;
    else
    if( distToLight > rend )
      falloff = 0.0;
      else
      {
      falloff = rend-distToLight / rend-rstart;
      }

    effectiveIntesity = d*lightIntensity * falloff;




    effectiveIntesity = d*lightIntensity * (1.0/(constFudge + (linearFudge*distToLight) +(quadraticFudge*distToLight*distToLight)));

  }
  else
  {
    effectiveIntesity = 0.0;
  }

  //TODO: change the colour of the pixel
  vec4 lightingColour = vec4(vec4( 1.0 ) * effectiveIntesity); //TODO: improve

  //gl_FragColor = lightingColour;

  //texturing stuff
  //http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/
  vec4 textureColour = vec4(texture2D(grabTexture, texCoord.xy)); // TODO: why is this blank?

  vec4 col = vec4(textureColour.rgb, 1.0);
  col.r *= 1.0;
  col.g *= 1.0;

  gl_FragColor = (col);

  //gl_FragColor = (lightingColour * lightWeighting) + (textureColour.rgb * textureWeighting);

  //simple check to make sure that shader compiles
  vec3 lP = vec3(abs(gl_LightSource[0].position.x/100), abs(gl_LightSource[0].position.y/100), abs(gl_LightSource[0].position.z/100));
  vec4 r = vec4( lP.x, lP.y, lP.z, 1.0 );
  gl_FragColor = r;

  gl_FragColor = (lightingColour * lightWeighting) + (r * textureWeighting);
}

Edit:

I've had a go at doing what was suggested by user111821 and this is where I am now. My new issue is trying to get the heat texture to render to confirm that the shader can use it, however doing so causes the skin texture to render.

The final version of this will need both textures being used with some lighting contributing to the final pixel values of the sphere.

Revised Fragment Shader:

const float     lightWeighting = 0.5;
const float     textureWeighting = 0.25;
const float     heatWeighting = 0.25;


const float     PI = 3.14;
const float     lightIntensity = 1.0;
const float     constFudge = 0.025;
const float     linearFudge = 0.025;
const float     quadraticFudge = 0.05;

float rend  = 250.0;
float rstart = 50.0;


uniform float       Intensity;
uniform sampler2D   grabTexture;
uniform sampler2D   HeatValues;

varying vec3        Normal;
varying vec3        Vertex;
varying vec2        texCoord;

void main(void)
{   
  vec3 pigPos = Vertex;
  vec3 normPigPos = normalize(pigPos);


  vec3 lightPos = vec3(gl_LightSource[0].position.xyz);

  vec3 normLightPos = normalize(lightPos);




  float effectiveIntesity;


  vec3 normNormal = normalize(vec3(Normal.xyz));

  vec3 t = vec3(pigPos - lightPos);

  float distToLight = length(t);

  float d = dot(normNormal.xyz, normLightPos.xyz);
  if(d > 0.0)//facing the light
  {
    float falloff;

    if( distToLight < rstart )
      falloff = 1.0;
    else
    if( distToLight > rend )
      falloff = 0.0;
      else
      {
      falloff = rend-distToLight / rend-rstart;
      }

    effectiveIntesity = d*lightIntensity * falloff;




    effectiveIntesity = d*lightIntensity * (1.0/(constFudge + (linearFudge*distToLight) +(quadraticFudge*distToLight*distToLight)));

  }
  else
  {
    effectiveIntesity = 0.0;
  }

  //TODO: change the colour of the pixel
  vec4 lightingColour = vec4(vec4( 1.0 ) * effectiveIntesity); //TODO: improve

  //gl_FragColor = lightingColour;

  //texturing stuff
  //http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/
  vec4 textureColour = vec4(texture2D(grabTexture, texCoord.xy)); // TODO: why is this blank?

  //vec4 col = vec4(textureColour.rgb, 1.0);
  //col.r *= 1.0;
  //col.g *= 1.0;

  gl_FragColor = (textureColour);


  vec4 heatColour = vec4(texture2D(HeatValues, texCoord.xy));

  gl_FragColor = heatColour;

  //gl_FragColor = (lightingColour * lightWeighting) + (textureColour * textureWeighting) + (heatColour * heatWeighting);

  //simple check to make sure that shader compiles
  vec3 lP = vec3(abs(gl_LightSource[0].position.x/100), abs(gl_LightSource[0].position.y/100), abs(gl_LightSource[0].position.z/100));
  vec4 r = vec4( lP.x, lP.y, lP.z, 1.0 );
  //gl_FragColor = r;

  //gl_FragColor = (lightingColour * lightWeighting) + (r * textureWeighting);
}

This a snippet of the class which is doing a bunch of C++ side of OpenG, the header file has been prepended for reference:

#pragma once
#include "stdafx.h"

class NA_Pig
{
private:
  object_type pigObj;
  unsigned char* rawLoadedTexture = NULL;
  unsigned char* heatValuesPsudoTexture = NULL;
  GLuint heatValuesTexID;
public:
  NA_Pig();
  ~NA_Pig();

  void draw();

  //void loadShader();

  vec3 pos;

  int pigShader = 0;

  void init();

};




void NA_Pig::draw()
{
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(pos.x, pos.y, pos.z);
  glShadeModel(GL_SMOOTH);

  extern cRenderClass graphics;

  //Shader Stuff
  //cShader *pList = graphics.ShaderInfo.getList();
  //int shdr = 1;

  //if (shdr >= graphics.ShaderInfo.shaderCount())
  //{
  //    printf("Error: Can't access requested shader\n");
  //    shdr = 0;
  //}

  //// enable shader program..
  //glUseProgram(pList[shdr].program());

  //glUniform1fv(glGetAttribLocation(shaderID, variableNameAsString), sizeOfArray, valueAsArray)
  //glUniform1f(glGetAttribLocation(shaderID, variableNameAsString), value);

  //glUseProgram(shaderID);













  //glutSolidSphere(12, 120, 2);
  int size = 50;
  glScaled(size, size, size);





  // MATERIAL PROPERTISE
  GLfloat ambient[4] = { 1.0,1.0,1.0,1.0 };
  GLfloat diffuse[4] = { 1.0,1.0,1.0,1.0 };
  GLfloat specular[4] = { 1.0,1.0,1.0,1.0 };

  //glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
  //glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse);
  //glMaterialfv(GL_FRONT, GL_SPECULAR, specular);

  // activate texture for application is shader
  //https://www.opengl.org/archives/resources/code/samples/glut_examples/examples/examples.html
  glEnable(GL_TEXTURE_2D);

  //apparently the shader needs this
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, rawLoadedTexture);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCREEN_WIDTH*SCREEN_HEIGHT, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, &heatValuesPsudoTexture);

  glActiveTexture(pigObj.id_texture); //the shader can use this texture
  glActiveTexture(heatValuesTexID);

  cShader *pList = graphics.ShaderInfo.getList();
  glUseProgram( pList[1].program()); //shader on: no pig // fixed, was vertex shader being empty, replaced it with intensity.vert
                    //shader off: ambient light only (tiny amount of diffuse or an illusion?)
  extern NA_MathsLib na_maths;
  float intensity = (float) na_maths.dice(100);

  glUniform1i(pList[1].get_grabLoc(), pigObj.id_texture);
  glUniform1f(pList[1].intensity(), intensity);
  glUniform1f(pList[1].get_heatValueGrabLoc(), heatValuesTexID);

  //glutSolidSphere(2, 15, 2);
  pigObj.render();

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_TEXTURE_2D);
  glUseProgram(0); //disable pig heatlamp shader
  glPopMatrix();

}

void NA_Pig::init()
{

  int x, y, comp;

  rawLoadedTexture =  stbi_load("Pig/pig.bmp", &x, &y, &comp, 4);
  if (rawLoadedTexture == NULL)
    std::cout << "NA_Pig::init - stbi_load returned null\n";

  //https://open.gl/textures
  //https://open.gl/content/code/c3_multitexture.txt

  //texturing stuff
  glActiveTexture(GL_TEXTURE1);
  GLuint tex;
  glGenTextures(1, &tex);
  glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, tex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, rawLoadedTexture);

  //heat lamp psudo texture
  glActiveTexture(GL_TEXTURE2);
  GLuint heat;
  glGenTextures(1, &heat);
  glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, heat);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  float heatColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, heatColor);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  heatValuesPsudoTexture = new unsigned char[SCREEN_HEIGHT*SCREEN_WIDTH];
  memset(heatValuesPsudoTexture, 255, sizeof(unsigned char)*SCREEN_HEIGHT*SCREEN_WIDTH);

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCREEN_WIDTH*SCREEN_HEIGHT, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, &heatValuesPsudoTexture);
  heatValuesTexID = GL_TEXTURE2;

  pigObj.objloader("Pig/pig.obj");
  pigObj.id_texture = GL_TEXTURE1; //https://gamedev.stackexchange.com/questions/144828/why-is-my-texture-not-applied-correctly-in-opengl-2-0-with-glsl

  extern NA_MathsLib na_maths;
  na_maths.seedDice();
}
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

It occurs to me that what you're trying to do may be a little more difficult than originally anticipated. But I think it could be done in a 2-pass way. First, create a texture with a 2:1 aspect ratio. (The actual resolution is up to you.) This will be an equirectangular projection of the surface of your sphere.

You would first create an offscreen framebuffer and attach the 2:1 texture to it that you will draw to. Draw a quad the size of the texture and in the fragment shader, at each point, project it back onto the sphere and get it's distance to the heat source. Calculate its temperature (or whatever you're calculating) there.

Next you draw the scene with the texture you just made projected onto the sphere.

When you start the next round, you use the existing texture as your "previous frame" and draw to a new texture into the offscreen frame buffer. You can actually ping-pong back and forth between 2 textures. It never needs to be read back to main memory if you do it this way.

\$\endgroup\$
4
  • \$\begingroup\$ this doesn't help me, my issue is how do I get the stuff out of the shader and put it in the next one. Also, I only need the most recent frame. \$\endgroup\$
    – Lupus590
    Jul 15, 2017 at 18:59
  • \$\begingroup\$ Fragment shaders only have one way to write out data - by writing it to one of the frame buffer's attachments - either the color buffer, the depth buffer, or whatever. So what happens when the shader writes out data, but in the next frame the camera view has changed and revealed a portion that wasn't written out previously? \$\endgroup\$ Jul 15, 2017 at 20:59
  • \$\begingroup\$ @Lupus590 I've completely rewritten my answer. Let me know if it addresses your issues. \$\endgroup\$ Jul 16, 2017 at 0:00
  • \$\begingroup\$ I've had a go at doing a variation of this and had partial success, I'm editing my OP to reflect this \$\endgroup\$
    – Lupus590
    Jul 17, 2017 at 14:38
0
\$\begingroup\$

For the values being stored for the next frame, as mentioned already, you may be able to do this with a texture.

do you really need to do the calculations in the shader? an easy option could be to do your calculations per-vertex in C++, and pass the values to the vertex shader, which can then interpolate them to the fragment shader for use in rendering.

You may also have an easier time storing values calculated in the vertex shader than fragment shader if you don't want to do the work in C++ - but I must admit I am not sure on the correct way to do this.

also, the falloff calculation looks a bit odd?

falloff = rend-distToLight / rend-rstart;

this is understood as the compiler as

falloff = rend - (distToLight / rend) - rstart;

and may result in something like 250 - 0.5 - 50 == 199.5 should perhaps be

falloff = (distToLight - rstart) / (rend - rstart)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .