/*---------------------------------------------------------------------------* Project: Horizon File: LightingAniso.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). * * (c) DMP, Inc. * konstantin.kolchin@dmprof.com */ #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; /* diffuse texture ID */ GLuint s_DiffTexName; /* bump texture ID */ GLuint s_BumpTexName; /* obj file loader class object */ dat_t kimono; /* texture collection name */ GLuint s_TexColl ; /* ExpHeap for app. */ nn::fnd::ExpHeap s_AppHeap; uptr s_HeapForGx; uptr s_HeapForMalloc; const u32 s_GxHeapSize = 0x400000; const u32 s_HeapSize = 0x200000; demo::RenderSystem s_RenderSystem; static void SetLutTable() { /* Schlick suggested that distribution function of anisotropic reflection is composed of two functions, one dependent on dot product NH, and other dependent on dot product of the angle between the projection of H on the tangent plane and the tangent vector. in microfacets theory, distribution function is not depended on wave length(R,G,B) essentially. but, this sample distribution term looked up by NH is depended on wave length. therefore, specular has a little blue. */ GLuint lutids[5] ; glGenTextures(5, lutids); GLfloat lut[512]; int j; memset(lut, 0, sizeof(lut)); /* to ignore 1st specular term, set 0. */ glBindTexture(GL_LUT_TEXTURE0_DMP, lutids[0]); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); /* set term depended on NH */ for (j = 0; j < 256; j++) lut[j] = 0.7f * z_schlick(0.7f, (float)j/256.f, true); for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = 0.7f * z_schlick(0.7f, 1.f, true) - lut[255]; glBindTexture(GL_LUT_TEXTURE1_DMP, lutids[1]); 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] = 0.7f * z_schlick(0.7f, (float)j/256.f, true); for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = 0.7f * z_schlick(0.7f, 1.f, true) - lut[255]; glBindTexture(GL_LUT_TEXTURE2_DMP, lutids[2]); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 0; j < 256; j++) lut[j] = z_schlick(0.5f, (float)j/256.f, true); for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = z_schlick(0.5f, 1.f, true) - lut[255]; glBindTexture(GL_LUT_TEXTURE3_DMP, lutids[3]); glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); /* set term dependent on dot product between the projection of H on the tangent plane and the tangent vector */ for (j = 0; j < 256; j++) lut[j] = a_schlick(0.015f, (float)j/256.f, true); for (j = 0; j < 255; j++) lut[j + 256] = lut[j + 1] - lut[j]; lut[255 + 256] = a_schlick(0.015f, 1.f, true) - lut[255]; glBindTexture(GL_LUT_TEXTURE4_DMP, lutids[4]); glTexImage1D(GL_LUT_TEXTURE4_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); } /* set lighting render state */ static void SetRenderState(void) { GLfloat ld0[] = {0.5f,0.5f,0.5f,1.f} ; GLfloat ls0[] = {0.7f,0.7f,0.7f,1.f} ; GLfloat ld1[] = {0.5f,0.5f,0.5f,1.f} ; GLfloat ls1[] = {0.7f,0.7f,0.7f,1.f} ; /* all light setting */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLighting.enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD0"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD1"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputSP"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputFR"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputRB"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputRG"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputRR"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD1"), GL_LIGHT_ENV_CP_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRB"), GL_LIGHT_ENV_NH_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRG"), GL_LIGHT_ENV_NH_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRR"), GL_LIGHT_ENV_NH_DMP); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD0"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD1"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleSP"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleFR"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRB"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRG"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRR"), 1.f); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledRefl"), GL_TRUE); /* when anisotropic lighting, distribution function is looked up by dot product between projection of half-vector onto tangent plane and tangent vector. HW specification require layer configuration setting7 in this case. another layer configuration setting can not be used. */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG7_DMP); 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.lutEnabledD0"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD1"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.clampHighlights"), GL_FALSE); 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_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].geomFactor0"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].geomFactor1"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].twoSideDiffuse"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD0"), 0); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerRR"), 1); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerRG"), 2); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerRB"), 3); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD1"), 4); 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); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].diffuse"), 1, ld1); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].specular0"), 1, ls1); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].specular1"), 1, ls1); /* light position settings */ nn::math::Matrix34 mv; nn::math::Vector3 camPos(0.f, 0.f, 1.5f); nn::math::Vector3 camUp(0.f, 1.f, 0.f); nn::math::Vector3 target(0.f, 0.f, 0.f); nn::math::MTX34LookAt(&mv, &camPos, &camUp, &target); nn::math::Vector4 lpos0(-1.f, 0.5f, 1.f, 1.f); nn::math::Vector4 lpos1(1.f, 0.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)); lpos = nn::math::Vector4(nn::math::VEC4Dot(&mv0, &lpos1), nn::math::VEC4Dot(&mv1, &lpos1), nn::math::VEC4Dot(&mv2, &lpos1), lpos1.w); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[1].position"), 1, static_cast(lpos)); /* set projection matrix */ nn::math::Matrix44 proj; nn::math::MTX44Frustum(&proj, -0.01f, 0.01f, -0.01f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.01f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.2f, 10.f); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uProjection"), 1, GL_TRUE, static_cast(proj)); /* texture and texture environment settings */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[1].samplerType"), GL_TEXTURE_2D); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_MULT_ADD_DMP); 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_TEXTURE0, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_SECONDARY_COLOR_DMP); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcAlpha"), GL_CONSTANT, GL_CONSTANT, GL_CONSTANT); /* lookup table settings */ SetLutTable(); } /* load objects */ static void LoadObjects(void) { bool use_alpha; /* load obj file geometry and diffuse texture */ loadDAT( "rom:/resources/kimono.dat", &kimono); /* load texture */ glGenTextures(1, &s_DiffTexName); glActiveTexture(GL_TEXTURE0); /* stored in the texture collection */ glBindTexture(GL_TEXTURE_2D, s_DiffTexName); /* limit texture sampling to the top level only */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); loadTexture( "rom:/resources/KIMONO01.tga", GL_TEXTURE_2D, 0, use_alpha, true); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* load bump texture */ glGenTextures(1, &s_BumpTexName); glActiveTexture(GL_TEXTURE1) ; /* stored in the texture collection */ glBindTexture(GL_TEXTURE_2D, s_BumpTexName); /* limit texture sampling to the top level only */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); loadTexture( "rom:/resources/KIMONOB0.tga", GL_TEXTURE_2D, 0, use_alpha, true); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* restore active texture to unit 0 to enable loading of object's diffuse texture */ glActiveTexture(GL_TEXTURE0); } static void UnloadObjects(void) { unloadDAT(&kimono); return; } int DrawFrame(void) { static int f = 0; s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); nn::math::Matrix34 mv, rot; nn::math::Vector3 camPos(0.f, 0.f, 1.5f); nn::math::Vector3 camUp(0.f, 1.f, 0.f); nn::math::Vector3 target(0.f, 0.f, 0.f); nn::math::MTX34LookAt(&mv, &camPos, &camUp, &target); nn::math::MTX34RotXYZDeg(&rot, 0.f, static_cast(-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 < kimono.obj_num; i++) { glBindBuffer(GL_ARRAY_BUFFER, kimono.posVB); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)kimono.obj[i].vtx_offset); glBindBuffer(GL_ARRAY_BUFFER, kimono.normVB); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)kimono.obj[i].nrm_offset); if (kimono.obj[i].tex_size) { glBindBuffer(GL_ARRAY_BUFFER, kimono.tangVB); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)kimono.obj[i].tgt_offset); glBindBuffer(GL_ARRAY_BUFFER, kimono.texVB); glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (void*)kimono.obj[i].tex_offset); glEnableVertexAttribArray(2); glEnableVertexAttribArray(3) ; glUniform1i(glGetUniformLocation(s_ProgramID, "uTangentEnabled"), GL_TRUE); } else { glDisableVertexAttribArray(2); glDisableVertexAttribArray(3); glUniform1i(glGetUniformLocation(s_ProgramID, "uTangentEnabled"), GL_FALSE); } for (unsigned j = kimono.obj[i].patch_offset; j < kimono.obj[i].patch_size + kimono.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] = kimono.patch[j].ambient[col]; md[col] = kimono.patch[j].diffuse[col]; ms[col] = kimono.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, kimono.idxVB); glDrawElements(GL_TRIANGLES, kimono.patch[j].elm_size, GL_UNSIGNED_SHORT, (GLvoid*)(kimono.patch[j].elm_offset + kimono.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); /* create and bind texture collection object to capture subsequent modifications in the texture state both lookup table-related one and ordinary (2D, cube) textures */ glGenTextures(1, &s_TexColl); glBindTexture(GL_TEXTURE_COLLECTION_DMP, s_TexColl); LoadObjects(); /* enable arrays that will be mapped to position and normal */ glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); 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(); }