Last active
November 23, 2023 02:59
-
-
Save FrankRicharrd/2fc71b1460a9089edd69c4ac1814dc95 to your computer and use it in GitHub Desktop.
Physically based rendering using openscenegraph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi.. do you have one for IBL?