/*---------------------------------------------------------------------------* Project: Horizon File: LightingFresnel.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 47228 $ *---------------------------------------------------------------------------*/ /* *------------------------------------------------------------ * Copyright(c) 2009-2010 by Digital Media Professionals Inc. * All rights reserved. *------------------------------------------------------------ * This source code is the confidential and proprietary * of Digital Media Professionals Inc. *------------------------------------------------------------ */ /* * Comments * -------- * * Light reflection in this sample consists of two parts - diffuse one * (simulating reflection from inside of cloth) modulated by texture and * anisotropic one (simulating reflection of the air-fiber interface). * * To render anisotropic appearance, we use Schlick's model described in * the following paper * * Christophe Schlick, An Inexpensive BRDF Model for Physically-Based * Rendering, Comput. Graph. Forum 13(3), 233-246 (1994). */ #include #include #include #include #include #include #include #include "demo.h" #include "Util.h" #include "Loader.h" #include #include "Memory.h" /* program id */ GLuint s_ProgramID; /* shader id */ GLuint s_ShaderID; /* lut texture ID */ GLuint s_LutIDs[4]; /* env texture ID */ GLuint s_EnvTexName; /* bump texture ID */ GLuint s_BumpTexName; /* obj file loader class object */ dat_t car; /* ExpHeap for app. */ nn::fnd::ExpHeap s_AppHeap; uptr s_HeapForGx; uptr s_HeapForMalloc; const u32 s_GxHeapSize = 0x800000; const u32 s_HeapSize = 0x400000; demo::RenderSystem s_RenderSystem; static void SetLutTable() { /* Beckmann function is used as distribution function. see Microfacet Reflection section in "ILLUMINATION MODELS IN MAESTRO LIGHTING" for details. distribution function means distribution ratio of microfacet normal(local normal) against surface normal(macroscopic normal). */ GLfloat lut[512]; int j; glGenTextures ( 4, s_LutIDs ) ; glBindTexture ( GL_TEXTURE_COLLECTION_DMP, s_LutIDs[0] ) ; memset(lut, 0, sizeof(lut)); for (j = 1; j < 128; j++) lut[j] = 0.85f * beckmann((float)j/128.f, 0.3f); for (j = 0; j < 127; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[127 + 256] = 1.f - lut[127]; glBindTexture(GL_LUT_TEXTURE0_DMP, s_LutIDs[1]); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 1; j < 128; j++) lut[j] = beckmann((float)j/128.f, 1.4f); for (j = 0; j < 127; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[127 + 256] = 1.f - lut[127]; glBindTexture(GL_LUT_TEXTURE1_DMP, s_LutIDs[2]); glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 0; j < 256; j++) lut[j] = r_fresnel((float)j/256.f); for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = r_fresnel(1.f) - lut[255]; glBindTexture(GL_LUT_TEXTURE2_DMP, s_LutIDs[3]); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD0"), 0); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD1"), 1); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerFR"), 2); } /* set lighting render state */ static void SetRenderState(void) { glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLighting.enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD0"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD1"), GL_FALSE); /* We use GL_TRUE below to make negative NV non-zero because negative NV values happen on silhouettes we need to do this trick because we use normal map(Change to GL_FALSE if normal map is not used) */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputFR"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD0"), GL_LIGHT_ENV_NH_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD1"), GL_LIGHT_ENV_NH_DMP); /* fresnel function is looked up by light incidence. an angle of incidence is be equivalent to angle of reflectance. so fresnel looked up by dot product of light vector and view vector. */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputFR"), GL_LIGHT_ENV_NV_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor0"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor1"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].twoSideDiffuse"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledRefl"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.clampHighlights"), GL_FALSE); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD0"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD1"), 1.f); /* below we make bit shift after sampling table of Fresnel reflection we have to do this because we do not have HDR(and sky has high intensity) */ glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleFR"), 2.f); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.fresnelSelector"), GL_LIGHT_ENV_PRI_SEC_ALPHA_FRESNEL_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG3_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD0"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD1"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.bumpMode"), GL_LIGHT_ENV_BUMP_AS_BUMP_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.bumpSelector"), GL_TEXTURE1); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.bumpRenorm"), GL_TRUE); SetLutTable(); GLfloat ms2[] = {0.7f, 0.01f, 0.25f, 1.f}; GLfloat ld0[] = {1.f, 1.f, 1.f, 1.f}; GLfloat ls0[] = {1.f, 1.f, 1.f, 1.f}; glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.specular1"), 1, ms2); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].diffuse"), 1, ld0); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular0"), 1, ls0); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular1"), 1, ls0); { /* Loading bump texture */ char filename[]= "rom:/resources/car_tsp0.tga"; int _lev = 0; glGenTextures(1, &s_BumpTexName); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, s_BumpTexName); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); bool bUseAlpha; loadTexture(filename, GL_TEXTURE_2D, _lev, bUseAlpha); } { /* loading environment */ char *env_tex[] = { "rom:/resources/tpx0002.tga", "rom:/resources/tnx0002.tga", "rom:/resources/tpy0002.tga", "rom:/resources/tny0002.tga", "rom:/resources/tpz0002.tga", "rom:/resources/tnz0002.tga" }; glGenTextures(1, &s_EnvTexName); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, s_EnvTexName); for (int face = 0; face < 6; face++) { bool use_alpha; loadTexture(env_tex[face], GL_TEXTURE_CUBE_MAP_POSITIVE_X+face,0, use_alpha, true, 0, GL_RGBA); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, 0); } glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_TEXTURE_CUBE_MAP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_ADD); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcRgb"), GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_SECONDARY_COLOR_DMP, GL_CONSTANT); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[1].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[1].combineRgb"), GL_INTERPOLATE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[1].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[1].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[1].srcRgb"), GL_TEXTURE0, GL_PREVIOUS, GL_FRAGMENT_SECONDARY_COLOR_DMP); } /* load objects */ static void LoadObjects(void) { loadDAT( "rom:/resources/supercar.dat", &car); return; } static void UnloadObjects(void) { unloadDAT(&car); return; } int DrawFrame(void) { static int f = 0; s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); nn::math::Matrix44 proj; nn::math::MTX44Frustum(&proj, -0.025f, 0.025f, -0.025f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.025f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.2f, 200.f); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uProjection"), 1, GL_TRUE, static_cast(proj)); float step = 0.5f; nn::math::Matrix44 id; nn::math::MTX44Identity(&id); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uWorld"), 1, GL_TRUE, static_cast(id)); nn::math::Matrix34 mv; nn::math::Vector3 camPos(0.f, 5.75f, 22.5f); nn::math::Vector3 camUp(-1.f, 0.f, 0.f); nn::math::Vector3 target(0.f, 0.f, 0.f); nn::math::MTX34LookAt(&mv, &camPos, &camUp, &target); nn::math::Vector4 lpos0(8.f, 8.5f, 1.f, 1.f); nn::math::Vector4 mv0(mv.m[0][0], mv.m[0][1], mv.m[0][2], mv.m[0][3]); nn::math::Vector4 mv1(mv.m[1][0], mv.m[1][1], mv.m[1][2], mv.m[1][3]); nn::math::Vector4 mv2(mv.m[2][0], mv.m[2][1], mv.m[2][2], mv.m[2][3]); nn::math::Vector4 lpos(nn::math::VEC4Dot(&mv0, &lpos0), nn::math::VEC4Dot(&mv1, &lpos0), nn::math::VEC4Dot(&mv2, &lpos0), lpos0.w); glUniform4fv ( glGetUniformLocation ( s_ProgramID, "dmp_FragmentLightSource[0].position" ), 1, static_cast(lpos)) ; nn::math::Matrix34 rot; nn::math::MTX34RotXYZDeg(&rot, 0.f, -step*f, 0.f); nn::math::MTX34Mult(&mv, &mv, &rot); nn::math::Matrix44 tmp(mv); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uModelView"), 1, GL_TRUE, static_cast(tmp)); for (int i = 0; i < car.obj_num; i++) { glBindBuffer(GL_ARRAY_BUFFER, car.posVB); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)car.obj[i].vtx_offset); glBindBuffer(GL_ARRAY_BUFFER, car.normVB); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)car.obj[i].nrm_offset); glBindBuffer(GL_ARRAY_BUFFER, car.tangVB); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)car.obj[i].tgt_offset); glBindBuffer(GL_ARRAY_BUFFER, car.texVB); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (void*)car.obj[i].tex_offset); for (unsigned j = car.obj[i].patch_offset; j < car.obj[i].patch_size + car.obj[i].patch_offset; j++) { GLfloat ma[4] = {0.f, 0.f, 0.f, 1.f}; GLfloat md[4] = {0.f, 0.f, 0.f, 1.f}; GLfloat ms[4] = {0.f, 0.f, 0.f, 1.f}; for (int col = 0; col < 3; col++) { ma[col] = car.patch[j].ambient[col]; md[col] = car.patch[j].diffuse[col]; ms[col] = car.patch[j].specular[col]; } glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.diffuse"), 1, md); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.specular0"), 1, ms); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.ambient"), 1, ma); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, car.idxVB); glDrawElements(GL_TRIANGLES, car.patch[j].elm_size, GL_UNSIGNED_SHORT, (GLvoid*)(car.patch[j].elm_offset + car.obj[i].elm_offset)); } } glFinish(); s_RenderSystem.SwapBuffers(); s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY1); s_RenderSystem.Clear(); s_RenderSystem.SwapBuffers(); s_RenderSystem.WaitVsync(NN_GX_DISPLAY_BOTH); f++; return !glGetError(); } /* initialization */ static int Initialize(void) { // fs initialization nn::fs::Initialize(); const size_t ROMFS_BUFFER_SIZE = 1024 * 64; static char buffer[ROMFS_BUFFER_SIZE]; NN_UTIL_PANIC_IF_FAILED( nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE)); s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() ); s_HeapForGx = reinterpret_cast(s_AppHeap.Allocate(s_GxHeapSize)); /* Initialize display */ s_RenderSystem.Initialize(s_HeapForGx, s_GxHeapSize); /* Create heap for malloc*/ s_HeapForMalloc = reinterpret_cast(s_AppHeap.Allocate(s_HeapSize)); setMemoryHeap(s_HeapForMalloc, s_HeapSize); glClearColor(0.36f, 0.42f, 0.5f, 1.0f); glClearDepthf(1.f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); /* create program and load & attach vertex shader */ s_ProgramID = glCreateProgram(); s_ShaderID = glCreateShader(GL_VERTEX_SHADER); nn::fs::FileReader file(L"rom:/shader.shbin"); size_t fileSize = file.GetSize(); void* buf = s_AppHeap.Allocate(fileSize); s32 read = file.Read(buf, fileSize); glShaderBinary(1, &s_ShaderID, GL_PLATFORM_BINARY_DMP, buf, read); file.Finalize(); s_AppHeap.Free(buf); glAttachShader(s_ProgramID, s_ShaderID); /* attach fixed-function fragment shader */ glAttachShader(s_ProgramID, GL_DMP_FRAGMENT_SHADER_DMP); glBindAttribLocation(s_ProgramID, 0, "aPosition"); glBindAttribLocation(s_ProgramID, 1, "aNormal"); glBindAttribLocation(s_ProgramID, 2, "aTang"); glBindAttribLocation(s_ProgramID, 3, "aTexCoord"); glLinkProgram(s_ProgramID); glValidateProgram(s_ProgramID); /* set program as current one to enable setting its uniforms */ glUseProgram(s_ProgramID); LoadObjects(); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3); return 0; } void nnMain(void) { // Call only nn::applet::Enable to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); /* initialization */ if (Initialize() >= 0) { /* set another render state */ SetRenderState(); /* Enter loop */ while (1) { (void)DrawFrame(); } } UnloadObjects(); /* shutdown_display */ s_RenderSystem.Finalize(); s_AppHeap.Free(reinterpret_cast(s_HeapForMalloc)); s_AppHeap.Free(reinterpret_cast(s_HeapForGx)); s_AppHeap.Finalize(); }