Skip to content

Instantly share code, notes, and snippets.

@FrankRicharrd
Last active November 23, 2023 02:59
Show Gist options
  • Save FrankRicharrd/2fc71b1460a9089edd69c4ac1814dc95 to your computer and use it in GitHub Desktop.
Save FrankRicharrd/2fc71b1460a9089edd69c4ac1814dc95 to your computer and use it in GitHub Desktop.
Physically based rendering using openscenegraph
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
in vec3 WorldPos;
in vec3 Normal;
// material parameters
uniform vec3 albedo;
uniform float metallic;
uniform float roughness;
uniform float ao;
struct PointLight {
vec3 position;
vec3 color;
};
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];
uniform vec3 camPos;
const float PI = 3.14159265359;
// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, 0.001); // prevent divide by zero for roughness=0.0 and NdotH=1.0
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// ----------------------------------------------------------------------------
void main()
{
vec3 N = normalize(Normal);
vec3 V = normalize(camPos - WorldPos);
// calculate reflectance at normal incidence; if dia-electric (like plastic) use F0
// of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow)
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// reflectance equation
vec3 Lo = vec3(0.0);
for(int i = 0; i < 4; ++i)
{
// calculate per-light radiance
vec3 L = normalize(pointLights[i].position - WorldPos);
vec3 H = normalize(V + L);
float distance = length(pointLights[i].position - WorldPos);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = pointLights[i].color * attenuation;
// Cook-Torrance BRDF
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0);
vec3 nominator = NDF * G * F;
float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
vec3 specular = nominator / max(denominator, 0.001); // prevent divide by zero for NdotV=0.0 or NdotL=0.0
// kS is equal to Fresnel
vec3 kS = F;
// for energy conservation, the diffuse and specular light can't
// be above 1.0 (unless the surface emits light); to preserve this
// relationship the diffuse component (kD) should equal 1.0 - kS.
vec3 kD = vec3(1.0) - kS;
// multiply kD by the inverse metalness such that only non-metals
// have diffuse lighting, or a linear blend if partly metal (pure metals
// have no diffuse light).
kD *= 1.0 - metallic;
// scale light by NdotL
float NdotL = max(dot(N, L), 0.0);
// add to outgoing radiance Lo
Lo += (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
}
// ambient lighting (note that the next IBL tutorial will replace
// this ambient lighting with environment lighting).
vec3 ambient = vec3(0.03) * albedo * ao;
vec3 color = ambient + Lo;
// HDR tonemapping
color = color / (color + vec3(1.0));
// gamma correct
color = pow(color, vec3(1.0/2.2));
FragColor = vec4(color, 1.0);
}
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 3) in vec4 aTexCoords;
out vec2 TexCoords;
out vec3 WorldPos;
out vec3 Normal;
uniform mat4 osg_ModelViewProjectionMatrix;
uniform mat4 osg_ModelViewMatrix;
uniform mat4 osg_ViewMatrixInverse;
uniform mat3 osg_NormalMatrix;
uniform mat4 osg_ViewMatrix;
void main()
{
TexCoords = aTexCoords.xy;
mat4 model = osg_ViewMatrixInverse * osg_ModelViewMatrix;
WorldPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = osg_ModelViewProjectionMatrix * vec4(aPos, 1.0);
}
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osg/Texture2D>
#include <osg/Camera>
#include <osgUtil/Optimizer>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osg\LineWidth>
#include <osg/io_utils>
#include <osg/Array>
#include <osg/MatrixTransform>
#include <osg/ShapeDrawable>
#include <osgGA/TrackballManipulator>
#include <osgGA/StateSetManipulator>
#include <osgViewer/Viewer>
#include <algorithm>
const int OSG_WIDTH = 1280;
const int OSG_HEIGHT = 960;
const osg::Vec4f FOG_COLOR = osg::Vec4f(0.6f, 0.6f, 0.7f, 1.f);
class LogFileHandler : public osg::NotifyHandler
{
public:
LogFileHandler(const char *filename) { }
void notify(osg::NotifySeverity severity, const char *message)
{
//if (severity == osg::NotifySeverity::ALWAYS)
std::cout << message << std::endl;
}
protected:
~LogFileHandler() { }
};
osg::ref_ptr<osg::Geode> CreateSphere()
{
osg::ref_ptr<osg::Geode> node = new osg::Geode();
osg::ref_ptr<osg::Vec2Array> uv = new osg::Vec2Array;
osg::ref_ptr<osg::Vec3Array> positions = new osg::Vec3Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP);
const unsigned int X_SEGMENTS = 64;
const unsigned int Y_SEGMENTS = 64;
const float PI = 3.14159265359;
for (unsigned int y = 0; y <= Y_SEGMENTS; ++y)
{
for (unsigned int x = 0; x <= X_SEGMENTS; ++x)
{
float xSegment = (float)x / (float)X_SEGMENTS;
float ySegment = (float)y / (float)Y_SEGMENTS;
float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);
float yPos = std::cos(ySegment * PI);
float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);
positions->push_back(osg::Vec3(xPos, yPos, zPos));
uv->push_back(osg::Vec2(xSegment, ySegment));
normals->push_back(osg::Vec3(xPos, yPos, zPos));
}
}
bool oddRow = false;
for (int y = 0; y < Y_SEGMENTS; ++y)
{
if (!oddRow) // even rows: y == 0, y == 2; and so on
{
for (int x = 0; x <= X_SEGMENTS; ++x)
{
indices->push_back(y * (X_SEGMENTS + 1) + x);
indices->push_back((y + 1) * (X_SEGMENTS + 1) + x);
}
}
else
{
for (int x = X_SEGMENTS; x >= 0; --x)
{
indices->push_back((y + 1) * (X_SEGMENTS + 1) + x);
indices->push_back(y * (X_SEGMENTS + 1) + x);
}
}
oddRow = !oddRow;
}
int indexCount = indices->size();
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->setVertexArray(positions.get());
geom->setNormalArray(normals.get(), osg::Array::Binding::BIND_PER_VERTEX);
geom->setTexCoordArray(0, uv.get(), osg::Array::Binding::BIND_PER_VERTEX);
//geom->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, 36));
geom->addPrimitiveSet(indices);
node->addDrawable(geom);
return node;
}
class ViewPosCBMultiLightsn : public osg::Uniform::Callback
{
osg::ref_ptr<osg::Camera> _cam;
public:
ViewPosCBMultiLightsn(osg::Camera* cam)
{
_cam = cam;
}
virtual void operator()(osg::Uniform* uniform,
osg::NodeVisitor* nv)
{
osg::Vec3 eye, center, up;
_cam->getViewMatrixAsLookAt(eye, center, up);
uniform->set(osg::Vec3(eye.x(), eye.y(), eye.z()));
}
};
std::string get_file_string(std::string filePath) {
std::ifstream ifs(filePath);
return std::string((std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>()));
}
float clamp(float x, float upper, float lower)
{
return min(upper, max(x, lower));
}
void CreateShaders(osg::Node * node, osg::MatrixTransform * rootTransForm, osg::Camera* _camera, float metallic, float roughness)
{
std::string vertSource = get_file_string("C:/Users/User/Documents/Visual Studio 2015/Projects/OSG Test/OSG Test/pbr.vert");
std::string fragSource = get_file_string("C:/Users/User/Documents/Visual Studio 2015/Projects/OSG Test/OSG Test/pbr.frag");
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader(new osg::Shader(osg::Shader::VERTEX,
vertSource));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT,
fragSource));
osg::StateSet* stateset = node->getOrCreateStateSet(); // replace inst with your osg::node
stateset->setAttributeAndModes(program.get());
osg::Vec3 eye, center, up;
_camera->getViewMatrixAsLookAt(eye, center, up);
stateset->addUniform(new osg::Uniform("albedo", osg::Vec3(0.5f, 0.0f, 0.0f)));
stateset->addUniform(new osg::Uniform("ao", 1.0f));
// lights
// ------
osg::Vec3 lightPositions[] = {
osg::Vec3(-10.0f, 10.0f, 10.0f),
osg::Vec3(10.0f, 10.0f, 10.0f),
osg::Vec3(-10.0f, -10.0f, 10.0f),
osg::Vec3(10.0f, -10.0f, 10.0f),
};
osg::Vec3 lightColors[] = {
osg::Vec3(300.0f, 300.0f, 300.0f),
osg::Vec3(300.0f, 300.0f, 300.0f),
osg::Vec3(300.0f, 300.0f, 300.0f),
osg::Vec3(300.0f, 300.0f, 300.0f)
};
osg::ref_ptr<osg::Uniform> viewPos = new osg::Uniform(
"camPos", eye);
viewPos->setUpdateCallback(new ViewPosCBMultiLightsn(_camera));
stateset->addUniform(viewPos.get());
stateset->addUniform(new osg::Uniform("metallic", metallic));
stateset->addUniform(new osg::Uniform("roughness", roughness));
for (unsigned int i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i)
{
std::string s = "pointLights[" + std::to_string(i) + "].position";
std::string c = "pointLights[" + std::to_string(i) + "].color";
stateset->addUniform(new osg::Uniform(s.c_str(), lightPositions[i]));
stateset->addUniform(new osg::Uniform(c.c_str(), lightColors[i]));
}
}
void CreateMultipleSpheres(osg::MatrixTransform* rootTrans, osg::Camera* cam)
{
int nrRows = 7;
int nrColumns = 7;
float spacing = 2.5;
osg::Matrix model = osg::Matrix::identity();
for (int row = 0; row < nrRows; ++row)
{
float metallic = (float)row / (float)nrRows;
for (int col = 0; col < nrColumns; ++col)
{
// we clamp the roughness to 0.025 - 1.0 as perfectly smooth surfaces (roughness of 0.0) tend to look a bit off
// on direct lighting.
float roughness = std::clamp((float)col / (float)nrColumns, 0.05f, 1.0f);
osg::Matrix mat = osg::Matrix::translate(osg::Vec3((col - (nrColumns / 2)) * spacing,
(row - (nrRows / 2)) * spacing,
0.0f));
auto sphere = CreateSphere();
CreateShaders(sphere, rootTrans, cam, metallic, roughness);
osg::ref_ptr<osg::MatrixTransform> sphereTrans = new osg::MatrixTransform;
sphereTrans->addChild(sphere);
sphereTrans->setMatrix(mat);
rootTrans->addChild(sphereTrans);
}
}
}
void CreateModel(osg::Group* root, osg::Camera* cam)
{
osg::ref_ptr<osg::MatrixTransform> trans = new osg::MatrixTransform;
CreateMultipleSpheres(trans, cam);
root->addChild(trans);
}
void CreateSingleView()
{
osgViewer::Viewer viewer;
osg::DisplaySettings::instance()->setNumMultiSamples(4);
osg::ref_ptr<osg::Group> root = new osg::Group();
auto camera = viewer.getCamera();
CreateModel(root, camera);
viewer.setSceneData(root.get());
//root->addChild(GetGridAndAxis1());
viewer.setUpViewInWindow(100, 100, OSG_WIDTH, OSG_HEIGHT);
/* depth settings */
osg::StateSet* state = root->getOrCreateStateSet();
state->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
camera->setClearColor(osg::Vec4(0, 0, 0, 1));
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto manip = new osgGA::TrackballManipulator();
manip->setVerticalAxisFixed(true);
manip->setAllowThrow(false);
camera->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
camera->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(true);
viewer.setCameraManipulator(manip);
osg::ref_ptr<osgGA::StateSetManipulator> statesetManipulator = new osgGA::StateSetManipulator(viewer.getCamera()->getStateSet());
viewer.addEventHandler(statesetManipulator.get());
viewer.realize();
while (!viewer.done()) {
viewer.frame();
}
}
int main()
{
#ifdef _WIN32
::SetProcessDPIAware(); // if Windows
#endif // _WIN32
osg::setNotifyLevel(osg::NotifySeverity::DEBUG_INFO);
osg::setNotifyHandler(new LogFileHandler("log.txt"));
CreateSingleView();
return 0;
}
@BenJ778
Copy link

BenJ778 commented Apr 6, 2022

Hi.. do you have one for IBL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment