glm::dvec4 StdCoating::indirectBrdf( std::vector<Raycast>& raycasts, const RayHitReport& report, const Raycast& incidentRay) const { // Emission glm::dvec4 emission = glm::dvec4(0.0); // Report's shorthands const glm::dvec3& pos = report.position; const glm::dvec3& tex = report.texCoord; const glm::dvec3& wallNormal = report.normal; const glm::dvec3& reflectOrig = report.reflectionOrigin; const glm::dvec3& refractOrig = report.refractionOrigin; const Material& currMaterial = *report.currMaterial; const Material& nextMaterial = *report.nextMaterial; const glm::dvec3& incident = incidentRay.direction; // StdCoating properties double rough = roughness(tex); double pRIdx = paintRefractiveIndex(tex); double entropy = Raycast::getEntropy(rough); glm::dvec4 paintFrag = paintColor(tex); glm::dvec3 pColor = glm::dvec3(paintFrag); double pOpa = paintFrag.a; // Leaved material properties double lRIdx = currMaterial.refractiveIndex(pos); // Entered material properties double eOpa = nextMaterial.opacity(pos); double eCond = nextMaterial.conductivity(pos); double eRIdx = nextMaterial.refractiveIndex(pos); glm::dvec3 eColor = nextMaterial.color(pos); // Reflection glm::dvec3 reflectNormal = getMicrofacetNormal( wallNormal, incident, rough); // Fresnel reflection double paintReflectRatio = computeReflexionRatio( lRIdx, pRIdx, incident, reflectNormal); glm::dvec4 reflectSample(0.0); glm::dvec4 diffuseSample(0.0); glm::dvec4 refractSample(0.0); // Paint if(pOpa > 0.0) { double paintReflectWeight = pOpa * paintReflectRatio; reflectSample += glm::dvec4(paintReflectWeight); double paintDiffWeight = pOpa * (1 - paintReflectRatio); diffuseSample += glm::dvec4(pColor * paintDiffWeight, paintDiffWeight); } if(pOpa < 1.0) { // Metal reflection double metalWeight = (1 - pOpa) * eCond; if(metalWeight > 0.0) { reflectSample += glm::dvec4(eColor * metalWeight, metalWeight); } if(eCond < 1.0) { // Insulator reflection double insulProb = (1 - pOpa) * (1 - eCond); double matReflectRatio = computeReflexionRatio( lRIdx, eRIdx, incident, reflectNormal); double insulReflectWeight = insulProb * matReflectRatio; reflectSample += glm::dvec4(insulReflectWeight); // Fully opaque insulator if(eOpa >= 1.0) { double matDiffWeight = insulProb * (1 - matReflectRatio); diffuseSample += glm::dvec4(eColor * matDiffWeight, matDiffWeight); } // Refraction else { double paintRefract = pOpa * (1 - paintReflectRatio); double insulRefract = insulProb * (1 - matReflectRatio); double refractWeight = paintRefract + insulRefract; glm::dvec3 refractColor = glm::mix(color::white, pColor, pOpa); refractSample += glm::dvec4(refractColor * refractWeight, refractWeight); } } } // Refraction if(refractSample.w > 0.0) { glm::dvec3 refractDir = computeRefraction( lRIdx, eRIdx, incident, reflectNormal); if(glm::dot(refractDir, wallNormal) < 0.0) { raycasts.push_back(Raycast( entropy, refractSample, refractOrig, refractDir)); } else { reflectSample += refractSample; } } // Diffuse if(diffuseSample.w > 0.0) { if(rough < 1.0) { glm::dvec3 diffuseNormal = getMicrofacetNormal( wallNormal, incident, 1.0); // Fully diffusive glm::dvec3 diffuseDir = glm::reflect( incident, diffuseNormal); raycasts.push_back(Raycast( Raycast::FULLY_DIFFUSE, diffuseSample, reflectOrig, diffuseDir)); } else { reflectSample += diffuseSample; } } // Reflection if(reflectSample.w > 0.0) { glm::dvec3 reflectDir = glm::reflect( incident, reflectNormal); raycasts.push_back(Raycast( entropy, reflectSample, reflectOrig, reflectDir)); } // No emission return emission; }
/*! loads an .a2mtl material file * @param filename the materials filename */ void a2ematerial::load_material(const string& filename_) { filename = filename_; // read mat data if(!file_io::file_to_buffer(filename, buffer)) return; const string mat_data = buffer.str(); // check if we have a xml (mat) file if(mat_data.length() < 5 || mat_data.substr(0, 5) != "<?xml") { log_error("invalid a2e-material file %s!", filename); return; } xmlDoc* doc = xmlReadMemory(mat_data.c_str(), (int)mat_data.size(), nullptr, (const char*)"UTF-8", 0); xmlNode* root = xmlDocGetRootElement(doc); size_t object_count = 0; size_t object_id = 0; a2ematerial::material* cur_material = nullptr; xmlNode* cur_node = nullptr; stack<xmlNode*> node_stack; node_stack.push(root); while(!node_stack.empty()) { cur_node = node_stack.top(); node_stack.pop(); if(cur_node->next != nullptr) node_stack.push(cur_node->next); if(cur_node->type == XML_ELEMENT_NODE) { xmlElement* cur_elem = (xmlElement*)cur_node; string node_name = (const char*)cur_elem->name; if(cur_node->children != nullptr) node_stack.push(cur_node->children); if(node_name == "a2e_material") { size_t version = x->get_attribute<size_t>(cur_elem->attributes, "version"); if(version != A2E_MATERIAL_VERSION) { log_error("wrong version %u in material %s - should be %u!", version, filename, A2E_MATERIAL_VERSION); return; } object_count = x->get_attribute<size_t>(cur_elem->attributes, "object_count"); } else if(node_name == "material") { // get material info size_t id = x->get_attribute<size_t>(cur_elem->attributes, "id"); string type = x->get_attribute<string>(cur_elem->attributes, "type"); string model = x->get_attribute<string>(cur_elem->attributes, "model"); // create material materials.push_back(*new material()); cur_material = &materials.back(); cur_material->id = (ssize_t)id; cur_material->mat_type = (type == "diffuse" ? MATERIAL_TYPE::DIFFUSE : (type == "parallax" ? MATERIAL_TYPE::PARALLAX : MATERIAL_TYPE::NONE)); cur_material->lm_type = (model == "phong" ? LIGHTING_MODEL::PHONG : (model == "ashikhmin_shirley" ? LIGHTING_MODEL::ASHIKHMIN_SHIRLEY : LIGHTING_MODEL::NONE)); switch(cur_material->mat_type) { case MATERIAL_TYPE::DIFFUSE: cur_material->mat = new diffuse_material(); ((diffuse_material*)cur_material->mat)->diffuse_texture = dummy_texture; ((diffuse_material*)cur_material->mat)->specular_texture = default_specular; ((diffuse_material*)cur_material->mat)->reflectance_texture = default_specular; break; case MATERIAL_TYPE::PARALLAX: cur_material->mat = new parallax_material(); ((parallax_material*)cur_material->mat)->diffuse_texture = dummy_texture; ((parallax_material*)cur_material->mat)->specular_texture = default_specular; ((parallax_material*)cur_material->mat)->reflectance_texture = default_specular; ((parallax_material*)cur_material->mat)->height_texture = dummy_texture; ((parallax_material*)cur_material->mat)->normal_texture = dummy_texture; break; case MATERIAL_TYPE::NONE: cur_material->mat = new material_object(); break; } switch(cur_material->lm_type) { case LIGHTING_MODEL::PHONG: cur_material->model = new phong_model(); break; case LIGHTING_MODEL::ASHIKHMIN_SHIRLEY: cur_material->model = new ashikhmin_shirley_model(); break; case LIGHTING_MODEL::NONE: log_error("unknown lighting model type \"%s\" (%d)!", model, cur_material->lm_type); return; } // get material data for(xmlNode* material_node = cur_node->children; material_node; material_node = material_node->next) { xmlElement* material_elem = (xmlElement*)material_node; string material_name = (const char*)material_elem->name; if(material_name == "texture") { string texture_filename = x->get_attribute<string>(material_elem->attributes, "file"); string texture_type_str = x->get_attribute<string>(material_elem->attributes, "type"); TEXTURE_TYPE texture_type = (TEXTURE_TYPE)0; if(texture_type_str == "diffuse") texture_type = TEXTURE_TYPE::DIFFUSE; else if(texture_type_str == "specular") texture_type = TEXTURE_TYPE::SPECULAR; else if(texture_type_str == "reflectance") texture_type = TEXTURE_TYPE::REFLECTANCE; else if(texture_type_str == "height") texture_type = TEXTURE_TYPE::HEIGHT; else if(texture_type_str == "normal") texture_type = TEXTURE_TYPE::NORMAL; else if(texture_type_str == "anisotropic") texture_type = TEXTURE_TYPE::ANISOTROPIC; else { log_error("unknown texture type %s!", texture_type_str.c_str()); return; } // type/mat/model checking switch(cur_material->mat_type) { case MATERIAL_TYPE::DIFFUSE: if(texture_type == TEXTURE_TYPE::HEIGHT || texture_type == TEXTURE_TYPE::NORMAL) { log_error("invalid texture type/tag \"%s\" for diffuse material!", texture_type_str.c_str()); continue; } break; case MATERIAL_TYPE::PARALLAX: // everything allowed for parallax-mapping case MATERIAL_TYPE::NONE: break; } switch(cur_material->lm_type) { case LIGHTING_MODEL::PHONG: if(texture_type == TEXTURE_TYPE::ANISOTROPIC) { log_error("invalid texture type/tag \"%s\" for phong material (anisotropic type is not allowed)!", texture_type_str.c_str()); continue; } break; case LIGHTING_MODEL::ASHIKHMIN_SHIRLEY: case LIGHTING_MODEL::NONE: break; } // get filtering and wrapping modes (if they are specified) GLenum wrap_s = GL_REPEAT, wrap_t = GL_REPEAT; // default values TEXTURE_FILTERING filtering = TEXTURE_FILTERING::AUTOMATIC; for(unsigned int i = 0; i < 2; i++) { const char* wrap_mode = (i == 0 ? "wrap_s" : "wrap_t"); GLenum& wrap_ref = (i == 0 ? wrap_s : wrap_t); if(x->is_attribute(material_elem->attributes, wrap_mode)) { string wrap_str = x->get_attribute<string>(material_elem->attributes, wrap_mode); if(wrap_str == "clamp_to_edge") wrap_ref = GL_CLAMP_TO_EDGE; else if(wrap_str == "repeat") wrap_ref = GL_REPEAT; else if(wrap_str == "mirrored_repeat") wrap_ref = GL_MIRRORED_REPEAT; else { log_error("unknown wrap mode \"%s\" for %s!", wrap_str.c_str(), wrap_mode); } } } if(x->is_attribute(material_elem->attributes, "filtering")) { string filtering_str = x->get_attribute<string>(material_elem->attributes, "filtering"); if(filtering_str == "automatic") filtering = TEXTURE_FILTERING::AUTOMATIC; else if(filtering_str == "point") filtering = TEXTURE_FILTERING::POINT; else if(filtering_str == "linear") filtering = TEXTURE_FILTERING::LINEAR; else if(filtering_str == "bilinear") filtering = TEXTURE_FILTERING::BILINEAR; else if(filtering_str == "trilinear") filtering = TEXTURE_FILTERING::TRILINEAR; else { log_error("unknown filtering mode \"%s\"!", filtering_str.c_str()); } } a2e_texture tex = t->add_texture(floor::data_path(texture_filename.c_str()), filtering, engine::get_anisotropic(), (GLint)wrap_s, (GLint)wrap_t); switch(texture_type) { case TEXTURE_TYPE::DIFFUSE: ((diffuse_material*)cur_material->mat)->diffuse_texture = tex; break; case TEXTURE_TYPE::SPECULAR: ((diffuse_material*)cur_material->mat)->specular_texture = tex; break; case TEXTURE_TYPE::REFLECTANCE: ((diffuse_material*)cur_material->mat)->reflectance_texture = tex; break; case TEXTURE_TYPE::HEIGHT: ((parallax_material*)cur_material->mat)->height_texture = tex; break; case TEXTURE_TYPE::NORMAL: ((parallax_material*)cur_material->mat)->normal_texture = tex; break; case TEXTURE_TYPE::ANISOTROPIC: if(cur_material->lm_type == LIGHTING_MODEL::ASHIKHMIN_SHIRLEY) { ((ashikhmin_shirley_model*)cur_material->model)->anisotropic_texture = tex; } break; } } // ashikhmin/shirley only else if(material_name == "ashikhmin_shirley" || material_name == "const_isotropic" || material_name == "const_anisotropic") { if(cur_material->lm_type != LIGHTING_MODEL::ASHIKHMIN_SHIRLEY) { log_error("<%s> is an ashikhmin/shirley lighting-model only tag!", material_name.c_str()); continue; } if(material_name == "ashikhmin_shirley") { // no attributes atm } else if(material_name == "const_anisotropic") { float2 roughness(1.0f); string roughness_str = x->get_attribute<string>(material_elem->attributes, "roughness"); vector<string> roughness_arr = core::tokenize(roughness_str, ','); if(roughness_arr.size() < 2 || roughness_arr[0].length() == 0 || roughness_arr[1].length() == 0) { log_error("invalid anisotropic roughness value \"%s\"!", roughness_str.c_str()); return; } if(cur_material->lm_type == LIGHTING_MODEL::ASHIKHMIN_SHIRLEY) { ashikhmin_shirley_model* as_model = (ashikhmin_shirley_model*)cur_material->model; as_model->anisotropic_roughness.set(stof(roughness_arr[0]), stof(roughness_arr[1])); } else { log_error("invalid lighting model type for \"const_anisotropic\"!"); return; } } } // parallax mapping only else if(material_name == "pom") { if(cur_material->mat_type != MATERIAL_TYPE::PARALLAX) { log_error("<pom> is a parallax-mapping only tag!"); continue; } ((parallax_material*)cur_material->mat)->parallax_occlusion = (x->get_attribute<string>(material_elem->attributes, "value") == "true" ? true : false); } } } else if(node_name == "object") { size_t material_id = x->get_attribute<size_t>(cur_elem->attributes, "material_id"); const material* mat = nullptr; try { mat = &get_material(material_id); } catch(...) { log_error("invalid object mapping for object #%d!", object_id); if(object_id == 0) return; // no default possible, abort // set to default and continue mat = mapping[0]->mat; } bool blending = false; if(x->is_attribute(cur_elem->attributes, "blending")) { blending = x->get_attribute<bool>(cur_elem->attributes, "blending"); } mapping[object_id] = new object_mapping((material*)mat, blending); object_id++; } } } if(object_id == 0) { log_error("at least one object mapping is required!"); return; } if(object_count < mapping.size()) { log_error("less object mappings specified than required by object count!"); return; } xmlFreeDoc(doc); xmlCleanupParser(); }
glm::dvec4 StdCoating::directBrdf( const LightCast& lightCast, const RayHitReport& report, const Raycast& eyeRay) const { glm::dvec4 sampleSum = glm::dvec4(0.0); // Report's shorthands const glm::dvec3& pos = report.position; const glm::dvec3& tex = report.texCoord; const glm::dvec3& incident = lightCast.raycast.direction; // StdCoating properties double rough = roughness(tex); double entropy = Raycast::getEntropy(rough); double pRIdx = paintRefractiveIndex(tex); glm::dvec4 paintFrag = paintColor(tex); double pOpa = paintFrag.a; // Material properties double lRIdx = report.currMaterial->refractiveIndex(pos); double eRIdx = report.nextMaterial->refractiveIndex(pos); double eCond = report.nextMaterial->conductivity(pos); double eOpa = report.nextMaterial->opacity(pos); // Geometry glm::dvec3 wallNormal = report.normal; glm::dvec3 outDir = -eyeRay.direction; double inDotNorm = -glm::dot(incident, wallNormal); double outDotNorm = glm::dot(outDir, wallNormal); bool isTransmission = outDotNorm < 0.0; // Microfacet glm::dvec3 halfVec = glm::normalize(outDir - incident); wallNormal = glm::normalize(glm::mix(wallNormal, halfVec, rough)); // Fresnel reflection double paintReflectRatio = computeReflexionRatio( lRIdx, pRIdx, incident, wallNormal); double matReflectRatio = computeReflexionRatio( lRIdx, eRIdx, incident, wallNormal); if(!isTransmission) { glm::dvec3 eColor = report.nextMaterial->color(pos); double diffuseLightSize = lightCast.diffuseSize( lightCast, eyeRay, Raycast::FULLY_DIFFUSE); double totalDiffScatt = (eOpa >= 1.0 ? 1.0 : 0.0); double metalProb = (1 - pOpa) * eCond; double insulProb = (1 - pOpa) * (1 - eCond); double matDiffProb = insulProb * totalDiffScatt * (1 - matReflectRatio); double diffuseProb = pOpa * (1 - paintReflectRatio) + matDiffProb; double fresnelProb = pOpa * paintReflectRatio + insulProb * matReflectRatio; double reflectProb = fresnelProb + metalProb; double diffuseIntens = inDotNorm * diffuseLightSize; if(diffuseProb > 0.0) { double diffuseWeight = diffuseProb * diffuseIntens; glm::dvec3 pColor = glm::dvec3(paintFrag); glm::dvec3 diffuseColor = glm::mix(pColor, eColor, matDiffProb / diffuseProb); glm::dvec4 diffSample(diffuseColor * diffuseWeight, diffuseWeight); sampleSum += diffSample; } if(reflectProb > 0.0 && rough > 0.0) { double reflectLightSize = lightCast.diffuseSize( lightCast, eyeRay, entropy); glm::dvec3 reflection = glm::reflect(incident, wallNormal); double outDotReflect = glm::max(glm::dot(outDir, reflection), 0.0); double reflectPower = cellar::fast_pow(outDotReflect, 1 / rough); double reflectIntens = reflectPower * reflectLightSize; reflectIntens = glm::mix(reflectIntens, diffuseIntens, rough); double reflectWeight = reflectProb * reflectIntens; glm::dvec3 reflectColor = glm::mix(color::white, eColor, metalProb / reflectProb); glm::dvec4 reflectSample(reflectColor * reflectWeight, reflectWeight); sampleSum += reflectSample; } } else if(rough > 0.0 && eOpa < 1.0) { double insulProb = (1 - pOpa) * (1 - eCond); if(insulProb > 0.0) { double reflectLightSize = lightCast.diffuseSize( lightCast, eyeRay, entropy); glm::dvec3 refraction = computeRefraction(lRIdx, eRIdx, incident, wallNormal); double refractDotOut = glm::max(glm::dot(refraction, outDir), 0.0); double refractPower = cellar::fast_pow(refractDotOut, 1 / rough); refractPower = glm::mix(refractPower, inDotNorm, rough); double paintRefract = pOpa * (1 - paintReflectRatio); double insulRefract = insulProb * (1 - matReflectRatio); double refractProb = (paintRefract + insulRefract) * refractPower * reflectLightSize; glm::dvec4 refractSample(refractProb); sampleSum += refractSample; } } return sampleSum; }