Compare commits
5 Commits
0a269449a3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f3d013c8e | |||
| 9110d689a5 | |||
| b290cee689 | |||
| 194726f823 | |||
| 44de2c7013 |
236
CLAUDE.md
Normal file
236
CLAUDE.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a cross-platform Shadertoy-like fragment shader viewer built with SDL3 + OpenGL 3.3. The application loads and displays GLSL shaders from the `shaders/` directory with runtime switching capabilities.
|
||||
|
||||
## Build and Development Commands
|
||||
|
||||
### Building (CMake - Development)
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
### Building (Makefile - Release Packages)
|
||||
```bash
|
||||
make windows_release # Creates .zip with DLLs
|
||||
make macos_release # Creates .dmg with app bundle
|
||||
make linux_release # Creates .tar.gz
|
||||
make show_version # Display build version (YYYY-MM-DD format)
|
||||
```
|
||||
|
||||
Platform-specific debug builds:
|
||||
```bash
|
||||
make windows_debug
|
||||
make macos_debug
|
||||
make linux_debug
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
```bash
|
||||
./shadertoy [SHADER_PATH] [-F|--fullscreen]
|
||||
|
||||
# Examples:
|
||||
./shadertoy shaders/test.frag.glsl
|
||||
./shadertoy -F shaders/fractal_pyramid.frag.glsl
|
||||
```
|
||||
|
||||
**Runtime Controls:**
|
||||
- `ESC` - Exit
|
||||
- `F3` - Toggle fullscreen
|
||||
- `LEFT/RIGHT ARROW` - Cycle through shaders in directory
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Design
|
||||
All application logic resides in `src/main.cpp` (~469 lines) - a monolithic design that's straightforward to understand. Key components:
|
||||
|
||||
1. **Shader Loading System** - Automatic directory scanning of `.glsl` files, sorted alphabetically
|
||||
2. **OpenGL Rendering** - Single fullscreen quad with fragment shader using GL_TRIANGLE_STRIP
|
||||
3. **Event Loop** - SDL3-based with vsync (SDL_GL_SwapWindow + 1ms delay)
|
||||
4. **Resource Path Resolution** - Multi-path fallback system for executable, relative, and macOS bundle paths
|
||||
|
||||
### Global State (main.cpp)
|
||||
```cpp
|
||||
shader_list_ // Vector of discovered .glsl shader paths
|
||||
current_shader_index_ // Active shader in rotation
|
||||
current_program_ // OpenGL shader program handle
|
||||
shader_start_ticks_ // Base time for iTime uniform calculation
|
||||
window_ // SDL3 window pointer
|
||||
shaders_directory_ // Shader directory path (resolved at startup)
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
- **SDL3** - Window/input management, OpenGL context
|
||||
- **GLAD** - OpenGL 3.3 loader (statically linked via `third_party/glad/`)
|
||||
- **C++17 stdlib** - filesystem, fstream, vector, algorithms
|
||||
|
||||
### Platform-Specific Code
|
||||
Uses preprocessor defines (`WINDOWS_BUILD`, `MACOS_BUILD`, `LINUX_BUILD`) for:
|
||||
- `getExecutableDirectory()` - Windows API, mach-o dyld, or /proc/self/exe
|
||||
- `getResourcesDirectory()` - Special macOS app bundle handling (Contents/Resources)
|
||||
- Linking flags - Windows uses static linking, macOS links OpenGL framework
|
||||
|
||||
## Shader System
|
||||
|
||||
### Shader Format (GLSL 3.3 Core)
|
||||
All shaders must follow this structure:
|
||||
|
||||
```glsl
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV; // Normalized [0,1] coordinates from vertex shader
|
||||
uniform vec2 iResolution; // Window resolution in pixels
|
||||
uniform float iTime; // Time since shader loaded (seconds)
|
||||
|
||||
// Shadertoy-style entry point
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
// fragCoord is in pixel coordinates
|
||||
// Your shader code here
|
||||
}
|
||||
|
||||
// Wrapper converts vUV to pixel coordinates
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
```
|
||||
|
||||
### Adding New Shaders
|
||||
1. Create `.glsl` file in `shaders/` directory (use `.frag.glsl` convention)
|
||||
2. Follow required format above
|
||||
3. File automatically appears in runtime shader rotation (arrow keys to navigate)
|
||||
4. **No code changes required** - directory is scanned at startup
|
||||
|
||||
### Shader Loading Pipeline
|
||||
1. Directory scan on startup (`scanShaderDirectory()`)
|
||||
2. Sort alphabetically
|
||||
3. Load fragment shader source from disk
|
||||
4. Compile vertex shader (fixed, embedded in main.cpp)
|
||||
5. Compile fragment shader with error logging
|
||||
6. Link program with error handling
|
||||
7. Update uniforms each frame (iResolution, iTime)
|
||||
|
||||
### Supported Shadertoy Features
|
||||
- ✅ `iTime` - Time uniform
|
||||
- ✅ `iResolution` - Window resolution (vec2, not vec3)
|
||||
- ✅ `mainImage()` function signature
|
||||
- ❌ `iMouse` - Not implemented
|
||||
- ❌ `iChannel0-3` - No texture channels (multi-pass not supported)
|
||||
- ❌ `iFrame`, `iTimeDelta`, `iDate` - Not implemented
|
||||
|
||||
### Converting from Shadertoy
|
||||
When porting Shadertoy shaders:
|
||||
1. Copy the `mainImage()` function as-is
|
||||
2. Add standard header (see format above)
|
||||
3. Add wrapper `main()` function
|
||||
4. Remove texture channel references (iChannel0-3)
|
||||
5. Change `iResolution.xy` to `iResolution` (this project uses vec2)
|
||||
|
||||
### Common Conversion Issues and Solutions
|
||||
|
||||
Based on experience converting complex shaders like ddla_light_tunnel:
|
||||
|
||||
#### iResolution vec3 vs vec2
|
||||
- **Shadertoy:** `iResolution` is `vec3(width, height, width/height)`
|
||||
- **This project:** `iResolution` is `vec2(width, height)`
|
||||
- **Solution:** Create vec3 manually: `vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y);`
|
||||
- **Then use:** `r.xy` for resolution, `r.y` for height (as in original)
|
||||
|
||||
#### Uninitialized Variables
|
||||
- **Problem:** Shadertoy code may have `vec3 rgb;` without initialization
|
||||
- **Shadertoy behavior:** Likely initializes to `vec3(0.0)` (black)
|
||||
- **This project:** Uninitialized variables contain undefined values (often causes black screen or wrong colors)
|
||||
- **Solution:** Always initialize: `vec3 rgb = vec3(0.0);`
|
||||
- **Wrong approach:** Don't use random noise unless shader explicitly uses iChannel texture for noise
|
||||
|
||||
#### Low mix() Factors Are Intentional
|
||||
- **Example:** `rgb = mix(rgb, calculated_color, 0.01);` means 1% new color, 99% existing
|
||||
- **Don't change these factors** - they create subtle effects intentionally
|
||||
- **If output is black:** Problem is likely the base value (rgb), not the mix factor
|
||||
|
||||
#### iChannel Textures
|
||||
- **Shadertoy shows iChannel0-3** in UI, but shader may not use them
|
||||
- **If iChannel3 has RGB noise but code doesn't reference it:** The noise is not actually used
|
||||
- **Check code for `texture(iChannelN, ...)` calls** - if none exist, ignore the iChannel setup
|
||||
- **Procedural noise replacement:** Only if shader explicitly samples the texture
|
||||
|
||||
#### Color Swapping Issues
|
||||
- **If colors are completely wrong:** Don't randomly swap color variables
|
||||
- **First verify:** Code matches original Shadertoy exactly (except required GLSL changes)
|
||||
- **Common mistake:** Changing color applications that were correct in original
|
||||
- **Debug approach:** Revert to exact original code, only modify for GLSL 3.3 compatibility
|
||||
|
||||
#### Division by Small Values - Color Overflow Artifacts
|
||||
- **Problem:** Division by values near zero causes extreme values → color overflow artifacts (green points, strange colors)
|
||||
- **Example:** `.01*vec4(6,2,1,0)/length(u*sin(iTime))` can divide by ~0.0 when near center and sin≈0
|
||||
- **Symptoms:** Bright white center that pulses to green, or random color artifacts in specific areas
|
||||
- **Root cause:** Even with tonemap (tanh/clamp), extreme intermediate values cause precision issues or saturation
|
||||
- **Solution - Double Protection:**
|
||||
1. **Add epsilon to denominator:** `max(denominator, 0.001)` prevents division by exact zero
|
||||
2. **Clamp the result:** `min(result, vec4(50.0))` prevents extreme accumulation
|
||||
3. **Only clamp problematic terms:** Don't clamp everything or scene becomes dim
|
||||
- **Example fix:**
|
||||
```glsl
|
||||
// Original (causes overflow):
|
||||
o += .01*vec4(6,2,1,0)/length(u*sin(t+t+t)) + 1./s * length(u);
|
||||
|
||||
// Fixed (prevents overflow while maintaining brightness):
|
||||
vec4 brightTerm = min(.01*vec4(6,2,1,0)/max(length(u*sin(t+t+t)), 0.001), vec4(50.0));
|
||||
o += brightTerm + 1./s * length(u);
|
||||
```
|
||||
- **Key values:** epsilon ~0.001 (not too small, not too large), clamp ~50.0 (allows brightness without explosion)
|
||||
- **Why this works in Shadertoy:** WebGL/browsers may handle float overflow differently than native OpenGL drivers
|
||||
|
||||
#### Debugging Black/Wrong Output
|
||||
1. **Check compilation errors first** - shader must compile without errors
|
||||
2. **Initialize all variables** - especially vec3 colors
|
||||
3. **Verify vec3 iResolution handling** - create vec3 from vec2 if needed
|
||||
4. **Don't modify mix factors** - keep original values
|
||||
5. **Compare with original code** - ensure logic is identical
|
||||
6. **Test progressive changes** - add header, test; add wrapper, test; etc.
|
||||
7. **Check for division by small values** - if you see color artifacts (especially green), look for divisions that can approach zero
|
||||
|
||||
## Build System Details
|
||||
|
||||
### Version Numbering
|
||||
Releases use build date format: `shadertoy-YYYY-MM-DD-{platform}` (auto-generated by Makefile)
|
||||
|
||||
### Release Artifacts
|
||||
- **Windows:** `.zip` with `shadertoy.exe` + `SDL3.dll` + shaders
|
||||
- **macOS:** `.dmg` with app bundle containing embedded SDL3.framework (arm64 only)
|
||||
- **Linux:** `.tar.gz` with binary + shaders
|
||||
|
||||
### Resource Bundling
|
||||
- Shaders copied to release packages
|
||||
- LICENSE and README.md included
|
||||
- Platform-specific dependencies bundled (DLLs on Windows, frameworks on macOS)
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Vertex Shader
|
||||
The vertex shader is hardcoded in `main.cpp` and creates a fullscreen quad. It:
|
||||
- Takes `vec2 aPos` (location 0) in NDC space [-1, 1]
|
||||
- Outputs `vec2 vUV` normalized to [0, 1]
|
||||
- No transformation matrices needed
|
||||
|
||||
### Shader Hot-Reloading
|
||||
Currently **not implemented**. Shader changes require application restart. The architecture would support adding this via file watching.
|
||||
|
||||
### Multi-Pass Rendering
|
||||
Single-pass only. To add multi-pass (BufferA/B/C like Shadertoy):
|
||||
- Create FBOs and textures per buffer
|
||||
- Render buffers in dependency order
|
||||
- Pass textures as `iChannel0-3` uniforms
|
||||
- Use ping-pong for feedback loops
|
||||
|
||||
### OpenGL Context
|
||||
Created via SDL3 with core profile (no deprecated functions). Context version: 3.3 core.
|
||||
@@ -22,6 +22,7 @@ set(APP_SOURCES
|
||||
# Fuentes de librerías de terceros
|
||||
set(EXTERNAL_SOURCES
|
||||
third_party/glad/src/glad.c
|
||||
third_party/jail_audio.cpp
|
||||
)
|
||||
|
||||
# Configuración de SDL3
|
||||
@@ -35,6 +36,7 @@ add_executable(${PROJECT_NAME} ${APP_SOURCES} ${EXTERNAL_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
"${CMAKE_SOURCE_DIR}/third_party/glad/include"
|
||||
"${CMAKE_SOURCE_DIR}/third_party"
|
||||
)
|
||||
|
||||
# Enlazar la librería SDL3
|
||||
|
||||
8
Makefile
8
Makefile
@@ -35,10 +35,11 @@ LINUX_RELEASE := $(TARGET_NAME)-$(VERSION)-linux.tar.gz
|
||||
# Lista completa de archivos fuente
|
||||
APP_SOURCES := \
|
||||
src/main.cpp \
|
||||
third_party/glad/src/glad.c
|
||||
third_party/glad/src/glad.c \
|
||||
third_party/jail_audio.cpp
|
||||
|
||||
# Includes
|
||||
INCLUDES := -Isrc -Ithird_party/glad/include
|
||||
INCLUDES := -Isrc -Ithird_party/glad/include -Ithird_party
|
||||
|
||||
# Variables según el sistema operativo
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@@ -94,6 +95,7 @@ windows_release:
|
||||
|
||||
# Copia la carpeta 'shaders'
|
||||
powershell Copy-Item -Path "shaders" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
||||
powershell Copy-Item -Path "data" -Destination "$(RELEASE_FOLDER)" -recurse -Force
|
||||
|
||||
# Copia los ficheros que están en la raíz del proyecto
|
||||
powershell Copy-Item "LICENSE" -Destination "$(RELEASE_FOLDER)"
|
||||
@@ -141,6 +143,7 @@ macos_release:
|
||||
|
||||
# Copia carpetas y ficheros
|
||||
cp -R shaders "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp -R data "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp -R release/frameworks/SDL3.xcframework "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Frameworks"
|
||||
cp release/icon.icns "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents/Resources"
|
||||
cp release/Info.plist "$(RELEASE_FOLDER)/$(APP_NAME).app/Contents"
|
||||
@@ -192,6 +195,7 @@ linux_release:
|
||||
|
||||
# Copia ficheros
|
||||
cp -R shaders "$(RELEASE_FOLDER)"
|
||||
cp -R data "$(RELEASE_FOLDER)"
|
||||
cp LICENSE "$(RELEASE_FOLDER)"
|
||||
cp README.md "$(RELEASE_FOLDER)"
|
||||
|
||||
|
||||
127
README.md
127
README.md
@@ -11,17 +11,27 @@ Proyecto minimal para ejecutar fragment shaders tipo Shadertoy usando SDL3 + GLA
|
||||
|
||||
|
||||
|
||||
## Características
|
||||
|
||||
- **Visualizador de shaders fragment** compatible con formato Shadertoy
|
||||
- **Contador de FPS** en barra de título
|
||||
- **Toggle VSync** con tecla F4
|
||||
- **Cambio de shaders en runtime** con flechas ←/→
|
||||
- **Sistema de metadata** en shaders (Name/Author automático en título)
|
||||
- **Sistema de feedback** para shaders que requieren frame anterior (opcional)
|
||||
- **Soporte de audio** con jail_audio (música de fondo)
|
||||
|
||||
|
||||
|
||||
## Estructura
|
||||
|
||||
- src/ — código fuente C++ (incluye main.cpp).
|
||||
|
||||
- shaders/ — shaders fragment (.frag.glsl) que se cargan en runtime.
|
||||
|
||||
- third_party/glad/ — glad.c + headers.
|
||||
|
||||
- CMakeLists.txt — configuración de build.
|
||||
|
||||
- .gitignore — recomendado.
|
||||
- src/ — código fuente C++ (incluye main.cpp)
|
||||
- shaders/ — shaders fragment (.frag.glsl) que se cargan en runtime
|
||||
- data/ — recursos (música, texturas, etc.)
|
||||
- third_party/glad/ — glad.c + headers
|
||||
- third_party/ — jail_audio y otras dependencias
|
||||
- CMakeLists.txt — configuración de build
|
||||
- Makefile — builds de release para Windows/macOS/Linux
|
||||
|
||||
|
||||
|
||||
@@ -63,55 +73,74 @@ brew install cmake sdl3
|
||||
|
||||
## Uso
|
||||
|
||||
Por defecto el ejecutable carga shaders/test.frag.glsl.
|
||||
Ejecutar con un shader específico:
|
||||
|
||||
./shadertoy shaders/fractal_pyramid.frag.glsl
|
||||
|
||||
|
||||
|
||||
Ejecutar en ventana:
|
||||
Ejecutar en fullscreen:
|
||||
|
||||
./shadertoy_sdl3 ../shaders/test.frag.glsl
|
||||
|
||||
|
||||
|
||||
Ejecutar en fullscreen nativo:
|
||||
|
||||
./shadertoy_sdl3 -F ../shaders/test.frag.glsl
|
||||
./shadertoy -F shaders/seascape.frag.glsl
|
||||
# o
|
||||
./shadertoy --fullscreen shaders/seascape.frag.glsl
|
||||
|
||||
|
||||
|
||||
Si ejecutas desde la raíz del repo:
|
||||
|
||||
./build/shadertoy_sdl3 shaders/frag_tile_iter.frag.glsl
|
||||
./build/shadertoy shaders/water.glsl
|
||||
|
||||
|
||||
|
||||
Atajos en ejecución
|
||||
### Atajos de teclado
|
||||
|
||||
- Escape — salir.
|
||||
|
||||
- F11 — alternar fullscreen desktop.
|
||||
- **ESC** — Salir de la aplicación
|
||||
- **F3** — Toggle fullscreen
|
||||
- **F4** — Toggle VSync (ON/OFF)
|
||||
- **← / →** — Cambiar al shader anterior/siguiente en la carpeta shaders/
|
||||
|
||||
|
||||
|
||||
## Formato de shader esperado
|
||||
|
||||
- #version 330 core
|
||||
|
||||
- Debe declarar:
|
||||
|
||||
uniform vec2 iResolution;
|
||||
|
||||
uniform float iTime;
|
||||
|
||||
in vec2 vUV;
|
||||
### Header obligatorio:
|
||||
```glsl
|
||||
// Name: Nombre del shader
|
||||
// Author: Autor
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
```
|
||||
|
||||
- Función de entrada esperada (opcional, el main llama a mainImage):
|
||||
### Función mainImage (estilo Shadertoy):
|
||||
```glsl
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
||||
// Tu código aquí
|
||||
// fragCoord está en píxeles
|
||||
vec2 uv = fragCoord / iResolution.xy;
|
||||
fragColor = vec4(uv, 0.5, 1.0);
|
||||
}
|
||||
```
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord);
|
||||
### Wrapper main():
|
||||
```glsl
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
```
|
||||
|
||||
- main() debe convertir vUV a fragCoord y llamar a mainImage.
|
||||
### Metadata opcional:
|
||||
- `// Name: Nombre` - Aparece en barra de título
|
||||
- `// Author: Autor` - Aparece en barra de título
|
||||
- `// iChannel3: self` - Habilita feedback (frame anterior)
|
||||
|
||||
|
||||
|
||||
@@ -133,15 +162,33 @@ Si obtienes "Failed to load fragment shader file", ejecuta desde la carpeta buil
|
||||
|
||||
|
||||
|
||||
## Multi-pass / Buffers (nota)
|
||||
## Compatibilidad con Shadertoy
|
||||
|
||||
Ejemplo actual es single-pass. Para portar Shadertoy con BufferA/B/C necesitas:
|
||||
### Características soportadas:
|
||||
✅ `iTime` - Tiempo en segundos
|
||||
✅ `iResolution` - Resolución de ventana (vec2)
|
||||
✅ `mainImage()` - Función de entrada estándar
|
||||
✅ Self-feedback - Frame anterior con `// iChannel3: self`
|
||||
|
||||
- crear FBOs y textures por buffer
|
||||
### No soportado actualmente:
|
||||
❌ `iMouse` - Interacción con ratón
|
||||
❌ `iChannel0-2` - Texturas externas
|
||||
❌ Multi-pass completo (BufferA/B/C/D)
|
||||
❌ `iFrame`, `iTimeDelta`, `iDate`
|
||||
|
||||
- renderizar buffers en orden y pasar las texturas como iChannelN a pases posteriores
|
||||
### Diferencias importantes:
|
||||
- **`iResolution`**: En Shadertoy es `vec3(width, height, width/height)`, aquí es `vec2(width, height)`
|
||||
- Solución: `vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y);`
|
||||
- **Inicialización de variables**: OpenGL nativo requiere inicializar variables explícitamente
|
||||
- **División por valores pequeños**: Puede causar overflow - usar `max(divisor, epsilon)`
|
||||
|
||||
- evitar leer y escribir la misma textura (usar ping-pong si hace falta)
|
||||
### Conversión de shaders de Shadertoy:
|
||||
1. Copiar función `mainImage()` tal cual
|
||||
2. Añadir header estándar (ver arriba)
|
||||
3. Añadir wrapper `main()`
|
||||
4. Eliminar referencias a `iChannel0-3` si no se usan
|
||||
5. Adaptar `iResolution` de vec3 a vec2 si es necesario
|
||||
6. Inicializar variables: `vec3 col = vec3(0.0);` en lugar de `vec3 col;`
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
data/music/485077__mat397__confused-voices.ogg
Normal file
BIN
data/music/485077__mat397__confused-voices.ogg
Normal file
Binary file not shown.
BIN
data/music/485078__mat397__polyflute-pad.ogg
Normal file
BIN
data/music/485078__mat397__polyflute-pad.ogg
Normal file
Binary file not shown.
BIN
data/music/485079__mat397__melancholic-flutes-pad.ogg
Normal file
BIN
data/music/485079__mat397__melancholic-flutes-pad.ogg
Normal file
Binary file not shown.
BIN
data/music/486083__mat397__world-of-ants-pad.ogg
Normal file
BIN
data/music/486083__mat397__world-of-ants-pad.ogg
Normal file
Binary file not shown.
BIN
data/music/618041__mat397__mangle-dark-pad.ogg
Normal file
BIN
data/music/618041__mat397__mangle-dark-pad.ogg
Normal file
Binary file not shown.
BIN
data/music/618042__mat397__bad-electric-dark-pad.ogg
Normal file
BIN
data/music/618042__mat397__bad-electric-dark-pad.ogg
Normal file
Binary file not shown.
41
data/music/_readme_and_license.txt
Normal file
41
data/music/_readme_and_license.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
Pack downloaded from Freesound
|
||||
----------------------------------------
|
||||
|
||||
"Ambient pads"
|
||||
|
||||
This Pack of sounds contains sounds by the following user:
|
||||
- Mat397 ( https://freesound.org/people/Mat397/ )
|
||||
|
||||
You can find this pack online at: https://freesound.org/people/Mat397/packs/27416/
|
||||
|
||||
|
||||
Licenses in this Pack (see below for individual sound licenses)
|
||||
---------------------------------------------------------------
|
||||
|
||||
Creative Commons 0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
Attribution 3.0: http://creativecommons.org/licenses/by/3.0/
|
||||
|
||||
|
||||
Sounds in this Pack
|
||||
-------------------
|
||||
|
||||
* 618042__mat397__bad-electric-dark-pad.wav.wav
|
||||
* url: https://freesound.org/s/618042/
|
||||
* license: Creative Commons 0
|
||||
* 618041__mat397__mangle-dark-pad.wav.wav
|
||||
* url: https://freesound.org/s/618041/
|
||||
* license: Creative Commons 0
|
||||
* 486083__mat397__world-of-ants-pad.wav.wav
|
||||
* url: https://freesound.org/s/486083/
|
||||
* license: Attribution 3.0
|
||||
* 485079__mat397__melancholic-flutes-pad.wav.wav
|
||||
* url: https://freesound.org/s/485079/
|
||||
* license: Attribution 3.0
|
||||
* 485078__mat397__polyflute-pad.wav.wav
|
||||
* url: https://freesound.org/s/485078/
|
||||
* license: Attribution 3.0
|
||||
* 485077__mat397__confused-voices.wav.wav
|
||||
* url: https://freesound.org/s/485077/
|
||||
* license: Attribution 3.0
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
release/icon.ico
BIN
release/icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 137 KiB |
BIN
release/icon.png
BIN
release/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 364 KiB |
BIN
release/icon/icon.afdesign
Normal file
BIN
release/icon/icon.afdesign
Normal file
Binary file not shown.
BIN
release/icon/icon.pxd
Normal file
BIN
release/icon/icon.pxd
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 MiB |
Binary file not shown.
Binary file not shown.
83
shaders/cineshader_lava.frag.glsl
Normal file
83
shaders/cineshader_lava.frag.glsl
Normal file
@@ -0,0 +1,83 @@
|
||||
// Name: Cineshader Lava
|
||||
// Author: edankwan
|
||||
// URL: https://www.shadertoy.com/view/3sySRK
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
float opSmoothUnion( float d1, float d2, float k )
|
||||
{
|
||||
float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
|
||||
return mix( d2, d1, h ) - k*h*(1.0-h);
|
||||
}
|
||||
|
||||
float sdSphere( vec3 p, float s )
|
||||
{
|
||||
return length(p)-s;
|
||||
}
|
||||
|
||||
float map(vec3 p)
|
||||
{
|
||||
float d = 2.0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
float fi = float(i);
|
||||
float time = iTime * (fract(fi * 412.531 + 0.513) - 0.5) * 2.0;
|
||||
d = opSmoothUnion(
|
||||
sdSphere(p + sin(time + fi * vec3(52.5126, 64.62744, 632.25)) * vec3(2.0, 2.0, 0.8), mix(0.5, 1.0, fract(fi * 412.531 + 0.5124))),
|
||||
d,
|
||||
0.4
|
||||
);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
vec3 calcNormal( in vec3 p )
|
||||
{
|
||||
const float h = 1e-5; // or some other value
|
||||
const vec2 k = vec2(1,-1);
|
||||
return normalize( k.xyy*map( p + k.xyy*h ) +
|
||||
k.yyx*map( p + k.yyx*h ) +
|
||||
k.yxy*map( p + k.yxy*h ) +
|
||||
k.xxx*map( p + k.xxx*h ) );
|
||||
}
|
||||
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||
{
|
||||
vec2 uv = fragCoord/iResolution.xy;
|
||||
|
||||
// screen size is 6m x 6m
|
||||
vec3 rayOri = vec3((uv - 0.5) * vec2(iResolution.x/iResolution.y, 1.0) * 6.0, 3.0);
|
||||
vec3 rayDir = vec3(0.0, 0.0, -1.0);
|
||||
|
||||
float depth = 0.0;
|
||||
vec3 p;
|
||||
|
||||
for(int i = 0; i < 64; i++) {
|
||||
p = rayOri + rayDir * depth;
|
||||
float dist = map(p);
|
||||
depth += dist;
|
||||
if (dist < 1e-6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
depth = min(6.0, depth);
|
||||
vec3 n = calcNormal(p);
|
||||
float b = max(0.0, dot(n, vec3(0.577)));
|
||||
vec3 col = (0.5 + 0.5 * cos((b + iTime * 3.0) + uv.xyx * 2.0 + vec3(0,2,4))) * (0.85 + b * 0.35);
|
||||
col *= exp( -depth * 0.15 );
|
||||
|
||||
// maximum thickness is 2m in alpha channel
|
||||
fragColor = vec4(col, 1.0 - (depth - 0.5) / 2.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name: Creation by Silexars
|
||||
// Author: Danguafer
|
||||
// URL: https://www.shadertoy.com/view/XsXXDn
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
|
||||
778
shaders/cube_lines.frag.glsl
Normal file
778
shaders/cube_lines.frag.glsl
Normal file
@@ -0,0 +1,778 @@
|
||||
// Name: Cube lines
|
||||
// Author: Danil
|
||||
// URL: https://www.shadertoy.com/view/NslGRN
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
// Note: Original shader uses iChannel0 for background blending (optional feature)
|
||||
// Since we don't support texture channels, that line is commented out
|
||||
// The shader works perfectly without it in default mode
|
||||
|
||||
// Created by Danil (2021+) https://cohost.org/arugl
|
||||
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
// self https://www.shadertoy.com/view/NslGRN
|
||||
|
||||
|
||||
// --defines for "DESKTOP WALLPAPERS" that use this shader--
|
||||
// comment or uncomment every define to make it work (add or remove "//" before #define)
|
||||
|
||||
|
||||
// this shadertoy use ALPHA, NO_ALPHA set alpha to 1, BG_ALPHA set background as alpha
|
||||
// iChannel0 used as background if alpha ignored by wallpaper-app
|
||||
//#define NO_ALPHA
|
||||
//#define BG_ALPHA
|
||||
//#define SHADOW_ALPHA
|
||||
//#define ONLY_BOX
|
||||
|
||||
|
||||
// save PERFORMANCE by disabling shadow
|
||||
//#define NO_SHADOW
|
||||
|
||||
|
||||
// static CAMERA position, 0.49 on top, 0.001 horizontal
|
||||
//#define CAMERA_POS 0.049
|
||||
|
||||
|
||||
// speed of ROTATION
|
||||
#define ROTATION_SPEED 0.8999
|
||||
|
||||
|
||||
// static SHAPE form, default 0.5
|
||||
//#define STATIC_SHAPE 0.15
|
||||
|
||||
|
||||
// static SCALE far/close to camera, 2.0 is default, exampe 0.5 or 10.0
|
||||
//#define CAMERA_FAR 0.1
|
||||
|
||||
|
||||
// ANIMATION shape change
|
||||
//#define ANIM_SHAPE
|
||||
|
||||
|
||||
// ANIMATION color change
|
||||
//#define ANIM_COLOR
|
||||
|
||||
|
||||
// custom COLOR, and change those const values
|
||||
//#define USE_COLOR
|
||||
const vec3 color_blue=vec3(0.5,0.65,0.8);
|
||||
const vec3 color_red=vec3(0.99,0.2,0.1);
|
||||
|
||||
|
||||
// use 4xAA for cube only (set 2-4-etc level of AA)
|
||||
//#define AA_CUBE 4
|
||||
|
||||
// use 4xAA for everything - derivative filtering will not be used, look fcos2
|
||||
// this is very slow - DO NOT USE
|
||||
//#define AA_ALL 4
|
||||
|
||||
|
||||
|
||||
// --shader code--
|
||||
|
||||
// Layers sorted and support transparency and self-intersection-transparency
|
||||
// Antialiasing is only dFd. (with some dFd fixes around edges)
|
||||
|
||||
// using iq's intersectors: https://iquilezles.org/articles/intersectors
|
||||
// using https://www.shadertoy.com/view/ltKBzG
|
||||
// using https://www.shadertoy.com/view/tsVXzh
|
||||
// using https://www.shadertoy.com/view/WlffDn
|
||||
// using https://www.shadertoy.com/view/WslGz4
|
||||
|
||||
#define tshift 53.
|
||||
|
||||
// reflect back side
|
||||
//#define backside_refl
|
||||
|
||||
// Camera with mouse
|
||||
// #define MOUSE_control (disabled - no iMouse support)
|
||||
|
||||
// min(iFrame,0) does not speedup compilation in ANGLE
|
||||
#define ANGLE_loops 0
|
||||
|
||||
|
||||
// this shader discover Nvidia bug with arrays https://www.shadertoy.com/view/NslGR4
|
||||
// use DEBUG with BUG, BUG trigger that bug and one layer will be white on Nvidia in OpenGL
|
||||
//#define DEBUG
|
||||
//#define BUG
|
||||
|
||||
#define FDIST 0.7
|
||||
#define PI 3.1415926
|
||||
#define GROUNDSPACING 0.5
|
||||
#define GROUNDGRID 0.05
|
||||
#define BOXDIMS vec3(0.75, 0.75, 1.25)
|
||||
|
||||
#define IOR 1.33
|
||||
|
||||
mat3 rotx(float a){float s = sin(a);float c = cos(a);return mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, c, s), vec3(0.0, -s, c)); }
|
||||
mat3 roty(float a){float s = sin(a);float c = cos(a);return mat3(vec3(c, 0.0, s), vec3(0.0, 1.0, 0.0), vec3(-s, 0.0, c));}
|
||||
mat3 rotz(float a){float s = sin(a);float c = cos(a);return mat3(vec3(c, s, 0.0), vec3(-s, c, 0.0), vec3(0.0, 0.0, 1.0 ));}
|
||||
|
||||
vec3 fcos1(vec3 x) {
|
||||
vec3 w = fwidth(x);
|
||||
//if((length(w)==0.))return vec3(0.); // dFd fix2
|
||||
//w*=0.; //test
|
||||
float lw=length(w);
|
||||
if((lw==0.)||isnan(lw)||isinf(lw)){vec3 tc=vec3(0.); for(int i=0;i<8;i++)tc+=cos(x+x*float(i-4)*(0.01*400./iResolution.y));return tc/8.;}
|
||||
|
||||
return cos(x) * smoothstep(3.14 * 2.0, 0.0, w);
|
||||
}
|
||||
|
||||
vec3 fcos2( vec3 x){return cos(x);}
|
||||
vec3 fcos( vec3 x){
|
||||
#ifdef AA_ALL
|
||||
return fcos2(x);
|
||||
#else
|
||||
return fcos1(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 getColor(vec3 p)
|
||||
{
|
||||
// dFd fix, dFd broken on borders, but it fix only top level dFd, self intersection has border
|
||||
//if (length(p) > 0.99)return vec3(0.);
|
||||
p = abs(p);
|
||||
|
||||
p *= 01.25;
|
||||
p = 0.5 * p / dot(p, p);
|
||||
#ifdef ANIM_COLOR
|
||||
p+=0.072*iTime;
|
||||
#endif
|
||||
|
||||
float t = (0.13) * length(p);
|
||||
vec3 col = vec3(0.3, 0.4, 0.5);
|
||||
col += 0.12 * fcos(6.28318 * t * 1.0 + vec3(0.0, 0.8, 1.1));
|
||||
col += 0.11 * fcos(6.28318 * t * 3.1 + vec3(0.3, 0.4, 0.1));
|
||||
col += 0.10 * fcos(6.28318 * t * 5.1 + vec3(0.1, 0.7, 1.1));
|
||||
col += 0.10 * fcos(6.28318 * t * 17.1 + vec3(0.2, 0.6, 0.7));
|
||||
col += 0.10 * fcos(6.28318 * t * 31.1 + vec3(0.1, 0.6, 0.7));
|
||||
col += 0.10 * fcos(6.28318 * t * 65.1 + vec3(0.0, 0.5, 0.8));
|
||||
col += 0.10 * fcos(6.28318 * t * 115.1 + vec3(0.1, 0.4, 0.7));
|
||||
col += 0.10 * fcos(6.28318 * t * 265.1 + vec3(1.1, 1.4, 2.7));
|
||||
col = clamp(col, 0., 1.);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
void calcColor(vec3 ro, vec3 rd, vec3 nor, float d, float len, int idx, bool si, float td, out vec4 colx,
|
||||
out vec4 colsi)
|
||||
{
|
||||
|
||||
vec3 pos = (ro + rd * d);
|
||||
#ifdef DEBUG
|
||||
float a = 1. - smoothstep(len - 0.15, len + 0.00001, length(pos));
|
||||
if (idx == 0)colx = vec4(1., 0., 0., a);
|
||||
if (idx == 1)colx = vec4(0., 1., 0., a);
|
||||
if (idx == 2)colx = vec4(0., 0., 1., a);
|
||||
if (si)
|
||||
{
|
||||
pos = (ro + rd * td);
|
||||
float ta = 1. - smoothstep(len - 0.15, len + 0.00001, length(pos));
|
||||
if (idx == 0)colsi = vec4(1., 0., 0., ta);
|
||||
if (idx == 1)colsi = vec4(0., 1., 0., ta);
|
||||
if (idx == 2)colsi = vec4(0., 0., 1., ta);
|
||||
}
|
||||
#else
|
||||
float a = 1. - smoothstep(len - 0.15*0.5, len + 0.00001, length(pos));
|
||||
//a=1.;
|
||||
vec3 col = getColor(pos);
|
||||
colx = vec4(col, a);
|
||||
if (si)
|
||||
{
|
||||
pos = (ro + rd * td);
|
||||
float ta = 1. - smoothstep(len - 0.15*0.5, len + 0.00001, length(pos));
|
||||
//ta=1.;
|
||||
col = getColor(pos);
|
||||
colsi = vec4(col, ta);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// xSI is self intersect data, fade to fix dFd on edges
|
||||
bool iBilinearPatch(in vec3 ro, in vec3 rd, in vec4 ps, in vec4 ph, in float sz, out float t, out vec3 norm,
|
||||
out bool si, out float tsi, out vec3 normsi, out float fade, out float fadesi)
|
||||
{
|
||||
vec3 va = vec3(0.0, 0.0, ph.x + ph.w - ph.y - ph.z);
|
||||
vec3 vb = vec3(0.0, ps.w - ps.y, ph.z - ph.x);
|
||||
vec3 vc = vec3(ps.z - ps.x, 0.0, ph.y - ph.x);
|
||||
vec3 vd = vec3(ps.xy, ph.x);
|
||||
t = -1.;
|
||||
tsi = -1.;
|
||||
si = false;
|
||||
fade = 1.;
|
||||
fadesi = 1.;
|
||||
norm=vec3(0.,1.,0.);normsi=vec3(0.,1.,0.);
|
||||
|
||||
float tmp = 1.0 / (vb.y * vc.x);
|
||||
float a = 0.0;
|
||||
float b = 0.0;
|
||||
float c = 0.0;
|
||||
float d = va.z * tmp;
|
||||
float e = 0.0;
|
||||
float f = 0.0;
|
||||
float g = (vc.z * vb.y - vd.y * va.z) * tmp;
|
||||
float h = (vb.z * vc.x - va.z * vd.x) * tmp;
|
||||
float i = -1.0;
|
||||
float j = (vd.x * vd.y * va.z + vd.z * vb.y * vc.x) * tmp - (vd.y * vb.z * vc.x + vd.x * vc.z * vb.y) * tmp;
|
||||
|
||||
float p = dot(vec3(a, b, c), rd.xzy * rd.xzy) + dot(vec3(d, e, f), rd.xzy * rd.zyx);
|
||||
float q = dot(vec3(2.0, 2.0, 2.0) * ro.xzy * rd.xyz, vec3(a, b, c)) + dot(ro.xzz * rd.zxy, vec3(d, d, e)) +
|
||||
dot(ro.yyx * rd.zxy, vec3(e, f, f)) + dot(vec3(g, h, i), rd.xzy);
|
||||
float r =
|
||||
dot(vec3(a, b, c), ro.xzy * ro.xzy) + dot(vec3(d, e, f), ro.xzy * ro.zyx) + dot(vec3(g, h, i), ro.xzy) + j;
|
||||
|
||||
if (abs(p) < 0.000001)
|
||||
{
|
||||
float tt = -r / q;
|
||||
if (tt <= 0.)
|
||||
return false;
|
||||
t = tt;
|
||||
// normal
|
||||
|
||||
vec3 pos = ro + t * rd;
|
||||
if(length(pos)>sz)return false;
|
||||
vec3 grad =
|
||||
vec3(2.0) * pos.xzy * vec3(a, b, c) + pos.zxz * vec3(d, d, e) + pos.yyx * vec3(f, e, f) + vec3(g, h, i);
|
||||
norm = -normalize(grad);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
float sq = q * q - 4.0 * p * r;
|
||||
if (sq < 0.0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = sqrt(sq);
|
||||
float t0 = (-q + s) / (2.0 * p);
|
||||
float t1 = (-q - s) / (2.0 * p);
|
||||
float tt1 = min(t0 < 0.0 ? t1 : t0, t1 < 0.0 ? t0 : t1);
|
||||
float tt2 = max(t0 > 0.0 ? t1 : t0, t1 > 0.0 ? t0 : t1);
|
||||
float tt0 = tt1;
|
||||
if (tt0 <= 0.)
|
||||
return false;
|
||||
vec3 pos = ro + tt0 * rd;
|
||||
// black border on end of circle and self intersection with alpha come because dFd
|
||||
// uncomment this to see or rename fcos2 to fcos
|
||||
//sz+=0.3;
|
||||
bool ru = step(sz, length(pos)) > 0.5;
|
||||
if (ru)
|
||||
{
|
||||
tt0 = tt2;
|
||||
pos = ro + tt0 * rd;
|
||||
}
|
||||
if (tt0 <= 0.)
|
||||
return false;
|
||||
bool ru2 = step(sz, length(pos)) > 0.5;
|
||||
if (ru2)
|
||||
return false;
|
||||
|
||||
// self intersect
|
||||
if ((tt2 > 0.) && ((!ru)) && !(step(sz, length(ro + tt2 * rd)) > 0.5))
|
||||
{
|
||||
si = true;
|
||||
fadesi=s;
|
||||
tsi = tt2;
|
||||
vec3 tpos = ro + tsi * rd;
|
||||
// normal
|
||||
vec3 tgrad = vec3(2.0) * tpos.xzy * vec3(a, b, c) + tpos.zxz * vec3(d, d, e) +
|
||||
tpos.yyx * vec3(f, e, f) + vec3(g, h, i);
|
||||
normsi = -normalize(tgrad);
|
||||
}
|
||||
|
||||
fade=s;
|
||||
t = tt0;
|
||||
// normal
|
||||
vec3 grad =
|
||||
vec3(2.0) * pos.xzy * vec3(a, b, c) + pos.zxz * vec3(d, d, e) + pos.yyx * vec3(f, e, f) + vec3(g, h, i);
|
||||
norm = -normalize(grad);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float dot2( in vec3 v ) { return dot(v,v); }
|
||||
|
||||
float segShadow( in vec3 ro, in vec3 rd, in vec3 pa, float sh )
|
||||
{
|
||||
float dm = dot(rd.yz,rd.yz);
|
||||
float k1 = (ro.x-pa.x)*dm;
|
||||
float k2 = (ro.x+pa.x)*dm;
|
||||
vec2 k5 = (ro.yz+pa.yz)*dm;
|
||||
float k3 = dot(ro.yz+pa.yz,rd.yz);
|
||||
vec2 k4 = (pa.yz+pa.yz)*rd.yz;
|
||||
vec2 k6 = (pa.yz+pa.yz)*dm;
|
||||
|
||||
for( int i=0; i<4 + ANGLE_loops; i++ )
|
||||
{
|
||||
vec2 s = vec2(i&1,i>>1);
|
||||
float t = dot(s,k4) - k3;
|
||||
|
||||
if( t>0.0 )
|
||||
sh = min(sh,dot2(vec3(clamp(-rd.x*t,k1,k2),k5-k6*s)+rd*t)/(t*t));
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
float boxSoftShadow( in vec3 ro, in vec3 rd, in vec3 rad, in float sk )
|
||||
{
|
||||
rd += 0.0001 * (1.0 - abs(sign(rd)));
|
||||
vec3 rdd = rd;
|
||||
vec3 roo = ro;
|
||||
|
||||
vec3 m = 1.0/rdd;
|
||||
vec3 n = m*roo;
|
||||
vec3 k = abs(m)*rad;
|
||||
|
||||
vec3 t1 = -n - k;
|
||||
vec3 t2 = -n + k;
|
||||
|
||||
float tN = max( max( t1.x, t1.y ), t1.z );
|
||||
float tF = min( min( t2.x, t2.y ), t2.z );
|
||||
|
||||
if( tN<tF && tF>0.0) return 0.0;
|
||||
|
||||
float sh = 1.0;
|
||||
sh = segShadow( roo.xyz, rdd.xyz, rad.xyz, sh );
|
||||
sh = segShadow( roo.yzx, rdd.yzx, rad.yzx, sh );
|
||||
sh = segShadow( roo.zxy, rdd.zxy, rad.zxy, sh );
|
||||
sh = clamp(sk*sqrt(sh),0.0,1.0);
|
||||
return sh*sh*(3.0-2.0*sh);
|
||||
}
|
||||
|
||||
float box(in vec3 ro, in vec3 rd, in vec3 r, out vec3 nn, bool entering)
|
||||
{
|
||||
rd += 0.0001 * (1.0 - abs(sign(rd)));
|
||||
vec3 dr = 1.0 / rd;
|
||||
vec3 n = ro * dr;
|
||||
vec3 k = r * abs(dr);
|
||||
|
||||
vec3 pin = -k - n;
|
||||
vec3 pout = k - n;
|
||||
float tin = max(pin.x, max(pin.y, pin.z));
|
||||
float tout = min(pout.x, min(pout.y, pout.z));
|
||||
if (tin > tout)
|
||||
return -1.;
|
||||
if (entering)
|
||||
{
|
||||
nn = -sign(rd) * step(pin.zxy, pin.xyz) * step(pin.yzx, pin.xyz);
|
||||
}
|
||||
else
|
||||
{
|
||||
nn = sign(rd) * step(pout.xyz, pout.zxy) * step(pout.xyz, pout.yzx);
|
||||
}
|
||||
return entering ? tin : tout;
|
||||
}
|
||||
|
||||
vec3 bgcol(in vec3 rd)
|
||||
{
|
||||
return mix(vec3(0.01), vec3(0.336, 0.458, .668), 1. - pow(abs(rd.z+0.25), 1.3));
|
||||
}
|
||||
|
||||
vec3 background(in vec3 ro, in vec3 rd , vec3 l_dir, out float alpha)
|
||||
{
|
||||
#ifdef ONLY_BOX
|
||||
alpha=0.;
|
||||
return vec3(0.01);
|
||||
#endif
|
||||
float t = (-BOXDIMS.z - ro.z) / rd.z;
|
||||
alpha=0.;
|
||||
vec3 bgc = bgcol(rd);
|
||||
if (t < 0.)
|
||||
return bgc;
|
||||
vec2 uv = ro.xy + t * rd.xy;
|
||||
#ifdef NO_SHADOW
|
||||
float shad=1.;
|
||||
#else
|
||||
float shad = boxSoftShadow((ro + t * rd), normalize(l_dir+vec3(0.,0.,1.))*rotz(PI*0.65) , BOXDIMS, 1.5);
|
||||
#endif
|
||||
float aofac = smoothstep(-0.95, .75, length(abs(uv) - min(abs(uv), vec2(0.45))));
|
||||
aofac = min(aofac,smoothstep(-0.65, 1., shad));
|
||||
float lght=max(dot(normalize(ro + t * rd+vec3(0.,-0.,-5.)), normalize(l_dir-vec3(0.,0.,1.))*rotz(PI*0.65)), 0.0);
|
||||
vec3 col = mix(vec3(0.4), vec3(.71,.772,0.895), lght*lght* aofac+ 0.05) * aofac;
|
||||
alpha=1.-smoothstep(7.,10.,length(uv));
|
||||
#ifdef SHADOW_ALPHA
|
||||
//alpha=clamp(alpha*max(lght*lght*0.95,(1.-aofac)*1.25),0.,1.);
|
||||
alpha=clamp(alpha*(1.-aofac)*1.25,0.,1.);
|
||||
#endif
|
||||
return mix(col*length(col)*0.8,bgc,smoothstep(7.,10.,length(uv)));
|
||||
}
|
||||
|
||||
#define swap(a,b) tv=a;a=b;b=tv
|
||||
|
||||
vec4 insides(vec3 ro, vec3 rd, vec3 nor_c, vec3 l_dir, out float tout)
|
||||
{
|
||||
tout = -1.;
|
||||
vec3 trd=rd;
|
||||
|
||||
vec3 col = vec3(0.);
|
||||
|
||||
float pi = 3.1415926;
|
||||
|
||||
if (abs(nor_c.x) > 0.5)
|
||||
{
|
||||
rd = rd.xzy * nor_c.x;
|
||||
ro = ro.xzy * nor_c.x;
|
||||
}
|
||||
else if (abs(nor_c.z) > 0.5)
|
||||
{
|
||||
l_dir *= roty(pi);
|
||||
rd = rd.yxz * nor_c.z;
|
||||
ro = ro.yxz * nor_c.z;
|
||||
}
|
||||
else if (abs(nor_c.y) > 0.5)
|
||||
{
|
||||
l_dir *= rotz(-pi * 0.5);
|
||||
rd = rd * nor_c.y;
|
||||
ro = ro * nor_c.y;
|
||||
}
|
||||
|
||||
#ifdef ANIM_SHAPE
|
||||
float curvature = (0.001+1.5-1.5*smoothstep(0.,8.5,mod((iTime+tshift)*0.44,20.))*(1.-smoothstep(10.,18.5,mod((iTime+tshift)*0.44,20.))));
|
||||
// curvature(to not const above) make compilation on Angle 15+ sec
|
||||
#else
|
||||
#ifdef STATIC_SHAPE
|
||||
const float curvature = STATIC_SHAPE;
|
||||
#else
|
||||
const float curvature = .5;
|
||||
#endif
|
||||
#endif
|
||||
float bil_size = 1.;
|
||||
vec4 ps = vec4(-bil_size, -bil_size, bil_size, bil_size) * curvature;
|
||||
vec4 ph = vec4(-bil_size, bil_size, bil_size, -bil_size) * curvature;
|
||||
|
||||
vec4 [3]colx=vec4[3](vec4(0.),vec4(0.),vec4(0.));
|
||||
vec3 [3]dx=vec3[3](vec3(-1.),vec3(-1.),vec3(-1.));
|
||||
vec4 [3]colxsi=vec4[3](vec4(0.),vec4(0.),vec4(0.));
|
||||
int [3]order=int[3](0,1,2);
|
||||
|
||||
for (int i = 0; i < 3 + ANGLE_loops; i++)
|
||||
{
|
||||
if (abs(nor_c.x) > 0.5)
|
||||
{
|
||||
ro *= rotz(-pi * (1. / float(3)));
|
||||
rd *= rotz(-pi * (1. / float(3)));
|
||||
}
|
||||
else if (abs(nor_c.z) > 0.5)
|
||||
{
|
||||
ro *= rotz(pi * (1. / float(3)));
|
||||
rd *= rotz(pi * (1. / float(3)));
|
||||
}
|
||||
else if (abs(nor_c.y) > 0.5)
|
||||
{
|
||||
ro *= rotx(pi * (1. / float(3)));
|
||||
rd *= rotx(pi * (1. / float(3)));
|
||||
}
|
||||
vec3 normnew;
|
||||
float tnew;
|
||||
bool si;
|
||||
float tsi;
|
||||
vec3 normsi;
|
||||
float fade;
|
||||
float fadesi;
|
||||
|
||||
if (iBilinearPatch(ro, rd, ps, ph, bil_size, tnew, normnew, si, tsi, normsi, fade, fadesi))
|
||||
{
|
||||
if (tnew > 0.)
|
||||
{
|
||||
vec4 tcol, tcolsi;
|
||||
calcColor(ro, rd, normnew, tnew, bil_size, i, si, tsi, tcol, tcolsi);
|
||||
if (tcol.a > 0.0)
|
||||
{
|
||||
{
|
||||
vec3 tvalx = vec3(tnew, float(si), tsi);
|
||||
dx[i]=tvalx;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
colx[i]=tcol;
|
||||
if (si)colxsi[i]=tcolsi;
|
||||
#else
|
||||
|
||||
float dif = clamp(dot(normnew, l_dir), 0.0, 1.0);
|
||||
float amb = clamp(0.5 + 0.5 * dot(normnew, l_dir), 0.0, 1.0);
|
||||
|
||||
{
|
||||
#ifdef USE_COLOR
|
||||
vec3 shad = 0.57 * color_blue * amb + 1.5*color_blue.bgr * dif;
|
||||
const vec3 tcr = color_red;
|
||||
#else
|
||||
vec3 shad = vec3(0.32, 0.43, 0.54) * amb + vec3(1.0, 0.9, 0.7) * dif;
|
||||
const vec3 tcr = vec3(1.,0.21,0.11);
|
||||
#endif
|
||||
float ta = clamp(length(tcol.rgb),0.,1.);
|
||||
tcol=clamp(tcol*tcol*2.,0.,1.);
|
||||
vec4 tvalx =
|
||||
vec4((tcol.rgb*shad*1.4 + 3.*(tcr*tcol.rgb)*clamp(1.-(amb+dif),0.,1.)), min(tcol.a,ta));
|
||||
tvalx.rgb=clamp(2.*tvalx.rgb*tvalx.rgb,0.,1.);
|
||||
tvalx*=(min(fade*5.,1.));
|
||||
colx[i]=tvalx;
|
||||
}
|
||||
if (si)
|
||||
{
|
||||
dif = clamp(dot(normsi, l_dir), 0.0, 1.0);
|
||||
amb = clamp(0.5 + 0.5 * dot(normsi, l_dir), 0.0, 1.0);
|
||||
{
|
||||
#ifdef USE_COLOR
|
||||
vec3 shad = 0.57 * color_blue * amb + 1.5*color_blue.bgr * dif;
|
||||
const vec3 tcr = color_red;
|
||||
#else
|
||||
vec3 shad = vec3(0.32, 0.43, 0.54) * amb + vec3(1.0, 0.9, 0.7) * dif;
|
||||
const vec3 tcr = vec3(1.,0.21,0.11);
|
||||
#endif
|
||||
float ta = clamp(length(tcolsi.rgb),0.,1.);
|
||||
tcolsi=clamp(tcolsi*tcolsi*2.,0.,1.);
|
||||
vec4 tvalx =
|
||||
vec4(tcolsi.rgb * shad + 3.*(tcr*tcolsi.rgb)*clamp(1.-(amb+dif),0.,1.), min(tcolsi.a,ta));
|
||||
tvalx.rgb=clamp(2.*tvalx.rgb*tvalx.rgb,0.,1.);
|
||||
tvalx.rgb*=(min(fadesi*5.,1.));
|
||||
colxsi[i]=tvalx;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// transparency logic and layers sorting
|
||||
float a = 1.;
|
||||
if (dx[0].x < dx[1].x){{vec3 swap(dx[0], dx[1]);}{int swap(order[0], order[1]);}}
|
||||
if (dx[1].x < dx[2].x){{vec3 swap(dx[1], dx[2]);}{int swap(order[1], order[2]);}}
|
||||
if (dx[0].x < dx[1].x){{vec3 swap(dx[0], dx[1]);}{int swap(order[0], order[1]);}}
|
||||
|
||||
tout = max(max(dx[0].x, dx[1].x), dx[2].x);
|
||||
|
||||
if (dx[0].y < 0.5)
|
||||
{
|
||||
a=colx[order[0]].a;
|
||||
}
|
||||
|
||||
#if !(defined(DEBUG)&&defined(BUG))
|
||||
|
||||
// self intersection
|
||||
bool [3] rul= bool[3](
|
||||
((dx[0].y > 0.5) && (dx[1].x <= 0.)),
|
||||
((dx[1].y > 0.5) && (dx[0].x > dx[1].z)),
|
||||
((dx[2].y > 0.5) && (dx[1].x > dx[2].z))
|
||||
);
|
||||
for(int k=0;k<3;k++){
|
||||
if(rul[k]){
|
||||
vec4 tcolxsi = vec4(0.);
|
||||
tcolxsi=colxsi[order[k]];
|
||||
vec4 tcolx = vec4(0.);
|
||||
tcolx=colx[order[k]];
|
||||
|
||||
vec4 tvalx = mix(tcolxsi, tcolx, tcolx.a);
|
||||
colx[order[k]]=tvalx;
|
||||
|
||||
vec4 tvalx2 = mix(vec4(0.), tvalx, max(tcolx.a, tcolxsi.a));
|
||||
colx[order[k]]=tvalx2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
float a1 = (dx[1].y < 0.5) ? colx[order[1]].a : ((dx[1].z > dx[0].x) ? colx[order[1]].a : 1.);
|
||||
float a2 = (dx[2].y < 0.5) ? colx[order[2]].a : ((dx[2].z > dx[1].x) ? colx[order[2]].a : 1.);
|
||||
col = mix(mix(colx[order[0]].rgb, colx[order[1]].rgb, a1), colx[order[2]].rgb, a2);
|
||||
a = max(max(a, a1), a2);
|
||||
return vec4(col, a);
|
||||
}
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord)
|
||||
{
|
||||
float osc = 0.5;
|
||||
vec3 l_dir = normalize(vec3(0., 1., 0.));
|
||||
l_dir *= rotz(0.5);
|
||||
float mouseY = 1.0 * 0.5 * PI;
|
||||
// Note: MOUSE_control disabled (no iMouse support)
|
||||
// #ifdef MOUSE_control
|
||||
// mouseY = (1.0 - 1.15 * iMouse.y / iResolution.y) * 0.5 * PI;
|
||||
// if(iMouse.y < 1.)
|
||||
// #endif
|
||||
#ifdef CAMERA_POS
|
||||
mouseY = PI*CAMERA_POS;
|
||||
#else
|
||||
mouseY = PI*0.49 - smoothstep(0.,8.5,mod((iTime+tshift)*0.33,25.))*(1.-smoothstep(14.,24.0,mod((iTime+tshift)*0.33,25.))) * 0.55 * PI;
|
||||
#endif
|
||||
#ifdef ROTATION_SPEED
|
||||
float mouseX = -2.*PI-0.25*(iTime*ROTATION_SPEED+tshift);
|
||||
#else
|
||||
float mouseX = -2.*PI-0.25*(iTime+tshift);
|
||||
#endif
|
||||
// Note: MOUSE_control disabled (no iMouse support)
|
||||
// #ifdef MOUSE_control
|
||||
// mouseX+=-(iMouse.x / iResolution.x) * 2. * PI;
|
||||
// #endif
|
||||
|
||||
#ifdef CAMERA_FAR
|
||||
vec3 eye = (2. + CAMERA_FAR) * vec3(cos(mouseX) * cos(mouseY), sin(mouseX) * cos(mouseY), sin(mouseY));
|
||||
#else
|
||||
vec3 eye = 4. * vec3(cos(mouseX) * cos(mouseY), sin(mouseX) * cos(mouseY), sin(mouseY));
|
||||
#endif
|
||||
vec3 w = normalize(-eye);
|
||||
vec3 up = vec3(0., 0., 1.);
|
||||
vec3 u = normalize(cross(w, up));
|
||||
vec3 v = cross(u, w);
|
||||
|
||||
vec4 tot=vec4(0.);
|
||||
#if defined(AA_CUBE)||defined(AA_ALL)
|
||||
#ifdef AA_CUBE
|
||||
const int AA = AA_CUBE;
|
||||
#else
|
||||
const int AA = AA_ALL;
|
||||
#endif
|
||||
vec3 incol_once=vec3(0.);
|
||||
bool in_once=false;
|
||||
vec4 incolbg_once=vec4(0.);
|
||||
bool bg_in_once=false;
|
||||
vec4 outcolbg_once=vec4(0.);
|
||||
bool bg_out_once=false;
|
||||
for( int mx=0; mx<AA; mx++ )
|
||||
for( int nx=0; nx<AA; nx++ )
|
||||
{
|
||||
vec2 o = vec2(mod(float(mx+AA/2),float(AA)),mod(float(nx+AA/2),float(AA))) / float(AA) - 0.5;
|
||||
vec2 uv = (fragCoord + o - 0.5 * iResolution.xy) / iResolution.x;
|
||||
#else
|
||||
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.x;
|
||||
#endif
|
||||
vec3 rd = normalize(w * FDIST + uv.x * u + uv.y * v);
|
||||
|
||||
vec3 ni;
|
||||
float t = box(eye, rd, BOXDIMS, ni, true);
|
||||
vec3 ro = eye + t * rd;
|
||||
vec2 coords = ro.xy * ni.z/BOXDIMS.xy + ro.yz * ni.x/BOXDIMS.yz + ro.zx * ni.y/BOXDIMS.zx;
|
||||
float fadeborders = (1.-smoothstep(0.915,1.05,abs(coords.x)))*(1.-smoothstep(0.915,1.05,abs(coords.y)));
|
||||
|
||||
if (t > 0.)
|
||||
{
|
||||
float ang = -iTime * 0.33;
|
||||
vec3 col = vec3(0.);
|
||||
#ifdef AA_CUBE
|
||||
if(in_once)col=incol_once;
|
||||
else{
|
||||
in_once=true;
|
||||
#endif
|
||||
float R0 = (IOR - 1.) / (IOR + 1.);
|
||||
R0 *= R0;
|
||||
|
||||
vec2 theta = vec2(0.);
|
||||
vec3 n = vec3(cos(theta.x) * sin(theta.y), sin(theta.x) * sin(theta.y), cos(theta.y));
|
||||
|
||||
vec3 nr = n.zxy * ni.x + n.yzx * ni.y + n.xyz * ni.z;
|
||||
vec3 rdr = reflect(rd, nr);
|
||||
float talpha;
|
||||
vec3 reflcol = background(ro, rdr, l_dir,talpha);
|
||||
|
||||
vec3 rd2 = refract(rd, nr, 1. / IOR);
|
||||
|
||||
float accum = 1.;
|
||||
vec3 no2 = ni;
|
||||
vec3 ro_refr = ro;
|
||||
|
||||
vec4 [2] colo = vec4[2](vec4(0.),vec4(0.));
|
||||
|
||||
for (int j = 0; j < 2 + ANGLE_loops; j++)
|
||||
{
|
||||
float tb;
|
||||
vec2 coords2 = ro_refr.xy * no2.z + ro_refr.yz * no2.x + ro_refr.zx * no2.y;
|
||||
vec3 eye2 = vec3(coords2, -1.);
|
||||
vec3 rd2trans = rd2.yzx * no2.x + rd2.zxy * no2.y + rd2.xyz * no2.z;
|
||||
|
||||
rd2trans.z = -rd2trans.z;
|
||||
vec4 internalcol = insides(eye2, rd2trans, no2, l_dir, tb);
|
||||
if (tb > 0.)
|
||||
{
|
||||
internalcol.rgb *= accum;
|
||||
colo[j]=internalcol;
|
||||
}
|
||||
|
||||
if ((tb <= 0.) || (internalcol.a < 1.))
|
||||
{
|
||||
float tout = box(ro_refr, rd2, BOXDIMS, no2, false);
|
||||
no2 = n.zyx * no2.x + n.xzy * no2.y + n.yxz * no2.z;
|
||||
vec3 rout = ro_refr + tout * rd2;
|
||||
vec3 rdout = refract(rd2, -no2, IOR);
|
||||
float fresnel2 = R0 + (1. - R0) * pow(1. - dot(rdout, no2), 1.3);
|
||||
rd2 = reflect(rd2, -no2);
|
||||
|
||||
#ifdef backside_refl
|
||||
if((dot(rdout, no2))>0.5){fresnel2=1.;}
|
||||
#endif
|
||||
ro_refr = rout;
|
||||
ro_refr.z = max(ro_refr.z, -0.999);
|
||||
|
||||
accum *= fresnel2;
|
||||
}
|
||||
}
|
||||
float fresnel = R0 + (1. - R0) * pow(1. - dot(-rd, nr), 5.);
|
||||
col = mix(mix(colo[1].rgb * colo[1].a, colo[0].rgb, colo[0].a)*fadeborders, reflcol, pow(fresnel, 1.5));
|
||||
col=clamp(col,0.,1.);
|
||||
#ifdef AA_CUBE
|
||||
}
|
||||
incol_once=col;
|
||||
if(!bg_in_once){
|
||||
bg_in_once=true;
|
||||
float alpha;
|
||||
incolbg_once = vec4(background(eye, rd, l_dir, alpha), 0.15);
|
||||
#if defined(BG_ALPHA)||defined(ONLY_BOX)||defined(SHADOW_ALPHA)
|
||||
incolbg_once.w = alpha;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float cineshader_alpha = 0.;
|
||||
cineshader_alpha = clamp(0.15*dot(eye,ro),0.,1.);
|
||||
vec4 tcolx = vec4(col, cineshader_alpha);
|
||||
#if defined(BG_ALPHA)||defined(ONLY_BOX)||defined(SHADOW_ALPHA)
|
||||
tcolx.w = 1.;
|
||||
#endif
|
||||
tot += tcolx;
|
||||
}
|
||||
else
|
||||
{
|
||||
vec4 tcolx = vec4(0.);
|
||||
#ifdef AA_CUBE
|
||||
if(!bg_out_once){
|
||||
bg_out_once=true;
|
||||
#endif
|
||||
float alpha;
|
||||
tcolx = vec4(background(eye, rd, l_dir, alpha), 0.15);
|
||||
#if defined(BG_ALPHA)||defined(ONLY_BOX)||defined(SHADOW_ALPHA)
|
||||
tcolx.w = alpha;
|
||||
#endif
|
||||
#ifdef AA_CUBE
|
||||
outcolbg_once=tcolx;
|
||||
}else tcolx=max(outcolbg_once,incolbg_once);
|
||||
#endif
|
||||
tot += tcolx;
|
||||
}
|
||||
#if defined(AA_CUBE)||defined(AA_ALL)
|
||||
}
|
||||
tot /= float(AA*AA);
|
||||
#endif
|
||||
fragColor = tot;
|
||||
#ifdef NO_ALPHA
|
||||
fragColor.w = 1.;
|
||||
#endif
|
||||
fragColor.rgb=clamp(fragColor.rgb,0.,1.);
|
||||
// Note: iChannel0 line removed (texture channel not supported)
|
||||
// Original line was for optional background blending when BG_ALPHA/ONLY_BOX/SHADOW_ALPHA defined
|
||||
// #if defined(BG_ALPHA)||defined(ONLY_BOX)||defined(SHADOW_ALPHA)
|
||||
// fragColor.rgb=fragColor.rgb*fragColor.w+texture(iChannel0, fragCoord/iResolution.xy).rgb*(1.-fragColor.w);
|
||||
// #endif
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
190
shaders/dbz.frag.glsl
Normal file
190
shaders/dbz.frag.glsl
Normal file
@@ -0,0 +1,190 @@
|
||||
// Name: New Leaked 3I/Atlas NASA Footage
|
||||
// Author: msm01
|
||||
// URL: https://www.shadertoy.com/view/3ftcRr
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
// A small improv/fanart from yesterday.
|
||||
|
||||
#define s(a,b,c) smoothstep(a,b,c)
|
||||
#define PI 3.14159
|
||||
#define NBCaps 3.
|
||||
|
||||
mat2 r2d( float a ){ float c = cos(a), s = sin(a); return mat2( c, s, -s, c ); }
|
||||
float metaDiamond(vec2 p, vec2 pixel, float r){ vec2 d = abs(p-pixel); return r / (d.x + d.y); }
|
||||
|
||||
// Dave Hoskins's hash ! Noone can hash hashes like he hashes !
|
||||
float hash12(vec2 p)
|
||||
{
|
||||
vec3 p3 = fract(vec3(p.xyx) * .1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
// Smoothed 1D-noise. Just like Zoltraak : stupid simple. Powerful.
|
||||
float fbm(in vec2 v_p)
|
||||
{
|
||||
float pvpx = v_p.x;
|
||||
vec2 V1 = vec2(floor(pvpx ));
|
||||
vec2 V2 = vec2(floor(pvpx + 1.0));
|
||||
return mix(hash12(V1),hash12(V2),smoothstep(0.0,1.0,fract(pvpx)));
|
||||
}
|
||||
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||
{
|
||||
// Get Base Coordinates
|
||||
vec2 p = vec2( (1.0/iResolution.y)*(fragCoord.x - iResolution.x/2.0),fragCoord.y / iResolution.y - 0.5);
|
||||
|
||||
// reversed, for accurate Martian perspective... :)
|
||||
p.x=-p.x;
|
||||
|
||||
// Zoom out
|
||||
p*=150.0;
|
||||
|
||||
// Init the Accumulator
|
||||
vec4 col = vec4(0.05,0.05,0.15,1.0);
|
||||
|
||||
// Make up other boxes and save base in them.
|
||||
vec2 save1 = p;
|
||||
vec2 save2 = p;
|
||||
|
||||
// Faint Nebula Background
|
||||
|
||||
// Tilt the camera
|
||||
p*= r2d(-0.05);
|
||||
// Space Background Gradient
|
||||
col = mix(col,vec4(0.2,0.3,0.5,1.0),smoothstep(75.0,0.0,abs(p.y - 5.0*fbm(vec2(0.01*(p.x - 33.333*iTime))) + 3.5)));
|
||||
// Untilt the camera
|
||||
p*= r2d( 0.05);
|
||||
|
||||
// BG Starfield
|
||||
|
||||
// Rotate
|
||||
p*= r2d(-0.05);
|
||||
// Zoom In
|
||||
p*= 0.35;
|
||||
// Scroll Left
|
||||
p+= vec2(-5.0*iTime,0.0);
|
||||
|
||||
// Hack the coords...
|
||||
vec2 b = fract(5.0*p);
|
||||
p = floor(5.0*p);
|
||||
// Draw the stars
|
||||
if( fbm(vec2(p.x*p.y)) > 0.996)col += clamp(1.0-pow(3.0*length(b+vec2(-0.5)),0.5),0.0,1.0);
|
||||
|
||||
// Reload because the coords are all f.. up now !
|
||||
p = save1;
|
||||
// Another Box...
|
||||
vec2 save3;
|
||||
|
||||
// We're going to draw max 4 capsules max.
|
||||
// Yes we could draw more but Earth must survive, man. Have mercy !
|
||||
float Nb_Capsules = clamp(NBCaps,0.0,4.0);
|
||||
for( float i = 0.0;i<Nb_Capsules; i++ )
|
||||
{
|
||||
// Reloooooaaaaad !
|
||||
p = save1;
|
||||
// Tilt as much as we tilted the stars...
|
||||
p*= r2d(-0.05);
|
||||
// Zoom out a bit
|
||||
p*=2.5;
|
||||
// Then zoom in a bit closer every loop.
|
||||
p*=1.0-0.25*i;
|
||||
// Compute Random Coordinates For Each Capsule Position
|
||||
// Move To That Position.
|
||||
p += vec2(150.0*fbm(vec2(0.15*iTime + i*54.321)) - 75.0, // X varies randomly
|
||||
50.0*sin( 0.25*iTime + i*54.321) - 25.0); // Y varies periodically
|
||||
// Save Position
|
||||
save3 = p;
|
||||
|
||||
// Mega Zoom
|
||||
p*=0.04;
|
||||
// (Ox)-axis Symetry for jets
|
||||
p.y = abs(p.y);
|
||||
|
||||
if( p.x > 0.0 )
|
||||
{
|
||||
// Green Jet
|
||||
col += vec4(0.0,1.0,0.5,1.0)*smoothstep(0.2,0.0,abs(p.y - 0.05*fbm(vec2(1.5*p.x - 40.0*iTime)))-0.05)*smoothstep(29.0,0.0,abs(p.x));
|
||||
// White Jet
|
||||
col += vec4(1.0,1.0,1.0,1.0)*smoothstep(0.1,0.0,abs(p.y - 0.05*fbm(vec2(1.5*p.x - 40.0*iTime)))-0.05)*smoothstep(29.0,0.0,abs(p.x));
|
||||
};
|
||||
|
||||
// Reload !
|
||||
p = save3;
|
||||
// (Ox)-axis Symetry for the flames
|
||||
p.y = abs(p.y);
|
||||
// Fine-tuning Flames position
|
||||
p+= vec2(-10.0,0.0);
|
||||
// Fine-tuning Flames Shape
|
||||
p *= vec2(0.75,1.0);
|
||||
|
||||
// Green Flames
|
||||
col += 0.8*vec4(0.0,1.0,0.5,1.0)*s(20.0,0.0,length(p)-25.0+7.0*sin(0.30*length(p)*atan(p.y,p.x) + 55.0*iTime));
|
||||
// White flames
|
||||
col += 0.8*vec4(1.0,1.0,1.0,1.0)*s(20.0,0.0,length(p)-20.0+7.0*sin(0.30*length(p)*atan(p.y,p.x) + 55.0*iTime));
|
||||
|
||||
p = save3;
|
||||
|
||||
// Fat Aura
|
||||
col = mix(col,vec4(1.0),0.5*s(10.0,0.0,length(p + vec2(5.0,0.0))-20.0)*abs(sin(50.0*iTime)));
|
||||
// Less-Fat Aura
|
||||
col = mix(col,vec4(1.0),0.5*s(20.0,0.0,length(p + vec2(5.0,0.0))-20.0));
|
||||
// Frieren : "Aura ? Shader yourself !"
|
||||
|
||||
// The Pod
|
||||
|
||||
// White Disk
|
||||
col = mix(col,vec4(1.0),s(0.01,0.0,length(p)-20.0));
|
||||
|
||||
if( length(p) - 20.0 < 0.0 ) // Basic Masking
|
||||
{
|
||||
// 2D Shading : bluish large shadow
|
||||
col = mix(col,vec4(0.65,0.68,0.68 + 0.1*(3.0-i),1.0),s(0.5,0.0,length(p - vec2(2.0,0.0))-17.0));
|
||||
// 2D Shading : dark small shadow
|
||||
// If Outside Porthole Zone
|
||||
if(s(0.0,1.0,length(vec2(3.0,2.0)*p + vec2(33.5,0.0))-23.0)>0.0)
|
||||
col = mix(col,vec4(0.45,0.55,0.55 + 0.1*(3.0-i),1.0),0.75*s(0.5,0.0,length(p - vec2(2.0,0.0)+ 0.5*fbm(vec2(4.5*atan(p.y,p.x))))-9.0));
|
||||
|
||||
// Small 2D Indentation Details On The Spheres Using A Procedural Texture
|
||||
// NOTE: Original used texture(iChannel0, ...) which is not supported
|
||||
// Texture detail removed - not essential for the effect
|
||||
// vec4 colorCapsule = vec4(hash12(0.0003*p*dot(p,p) + 0.789*i));
|
||||
// if(colorCapsule.x>0.75)if(s(0.0,1.0,length(vec2(3.0,2.0)*p + vec2(33.5,0.0))-23.0)>0.0)col *= vec4(0.25,0.25,0.25,1.0);
|
||||
|
||||
// Bigger Dark Line All Around The Pod
|
||||
col = mix(col,vec4(0.0),s(0.2,0.0,abs(length(p)-19.9)-0.20));
|
||||
// Draw The Porthole :
|
||||
col = mix(col,vec4(0.5,0.2,0.3,1.0) // Base Color
|
||||
-s(5.0,0.0,length(p + vec2(-6.0,15.0))-20.0) // Main Shadow
|
||||
-s(0.25,0.0,abs(length(p + vec2(0.0,3.0))-15.0)-0.4)// Vertical Shadow
|
||||
-s(0.0,1.5,p.y-8.5) // top Shadow
|
||||
+0.25*vec4(1.0,0.5,0.0,1.0)*s(10.0,0.0,abs(p.y)) // Fake Glass Gradient
|
||||
,
|
||||
s(0.5,0.0,length(vec2(3.0,2.0)*p + vec2(35.0,0.0))-19.9));
|
||||
|
||||
// Porthole Black Rings
|
||||
// Internal
|
||||
col = mix(col,vec4(0.0,0.0,0.0,1.0),s(1.0,0.0,abs(length(vec2(3.0,2.0)*p + vec2(35.0,0.0))-19.9)-0.1));
|
||||
// External
|
||||
col = mix(col,vec4(0.0,0.0,0.0,1.0),s(1.0,0.0,abs(length(vec2(3.0,2.0)*p + vec2(33.5,0.0))-23.0)-0.1));
|
||||
|
||||
// Pod Tennis-Ball Door Line...
|
||||
if(p.y>0.0)col = mix(col,vec4(0.0,0.0,0.0,1.0),s(1.0,0.0,abs(length(vec2(3.0,2.0)*p + vec2( 29.0,0.0))-30.0)-0.1));
|
||||
if(p.y<0.0)col = mix(col,vec4(0.0,0.0,0.0,1.0),s(1.0,0.0,abs(length(vec2(3.0,2.0)*p + vec2(-31.0,0.0))-30.0)-0.1));
|
||||
};
|
||||
};
|
||||
// WAKE UP SHEEPLE !
|
||||
fragColor = clamp(col,0.0,1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name: Fractal Pyramid
|
||||
// Author: bradjamesgrant
|
||||
// URL: https://www.shadertoy.com/view/tsXBzS
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
|
||||
159
shaders/just_another_cube.frag.glsl
Normal file
159
shaders/just_another_cube.frag.glsl
Normal file
@@ -0,0 +1,159 @@
|
||||
// Name: Just Another Cube
|
||||
// Author: mrange
|
||||
// URL: https://www.shadertoy.com/view/3XdXRr
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
// CC0: Just another cube
|
||||
// Glowtracers are great for compact coding, but I wanted to see how much
|
||||
// I could squeeze a more normal raymarcher in terms of characters used.
|
||||
|
||||
// Twigl: https://twigl.app?ol=true&ss=-OW-y9xgRgWubwKcn0Nd
|
||||
|
||||
// == Globals ==
|
||||
// Single-letter variable names are used to save characters (code golfing).
|
||||
mat2 R; // A 2D rotation matrix, calculated once per frame in mainImage and used by D.
|
||||
float d=1. // Stores the most recent distance to the scene from the ray's position.
|
||||
, z=0. // Stores the total distance traveled along the ray (initialized to avoid undefined behavior)
|
||||
, G=9. // "Glow" variable. Tracks the closest the ray comes to the object (for volumetric glow effect).
|
||||
, M=1e-3
|
||||
;
|
||||
// == Distance Function (SDF - Signed Distance Field) ==
|
||||
// This function calculates the shortest distance from a given point 'p' to the scene geometry.
|
||||
// A positive result means the point is outside an object, negative is inside, and zero is on the surface.
|
||||
// This is the core of "raymarching", as it tells us the largest safe step we can take along a ray.
|
||||
float D(vec3 p) {
|
||||
// Apply two rotations to the point's coordinates. This twists the space the object
|
||||
// exists in, making the simple cube shape appear more complex and animated.
|
||||
p.xy *= R;
|
||||
p.xz *= R;
|
||||
|
||||
// Create a higher-frequency version of the coordinate for detailed surface patterns.
|
||||
vec3 S = sin(123.*p);
|
||||
|
||||
// This creates a volumetric glow effect by tracking the minimum distance
|
||||
// to either the existing glow value or a glowing shell around the object.
|
||||
G = min(
|
||||
G
|
||||
// The glowing shell
|
||||
, max(
|
||||
abs(length(p)-.6)
|
||||
// The main object distance calculation:
|
||||
// 1. A superquadric (rounded cube shape) is created using an L8-norm.
|
||||
// The expression `pow(dot(p=p*p*p*p,p),.125)` is a golfed version of
|
||||
// `pow(pow(p.x,8)+pow(p.y,8)+pow(p.z,8), 1./8.)`.
|
||||
// The `- .5` defines the object's size.
|
||||
, d = pow(dot(p*=p*p*p,p),.125) - .5
|
||||
// 2. Surface detail subtraction. This creates small surface variations
|
||||
// using high-frequency sine waves for more appealing reflections.
|
||||
- pow(1.+S.x*S.y*S.z,8.)/1e5
|
||||
)
|
||||
);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
// == Main Render Function ==
|
||||
// This function is called for every pixel on the screen to determine its color.
|
||||
// 'o' is the final output color (rgba). 'C' is the input pixel coordinate (xy).
|
||||
void mainImage(out vec4 o, vec2 C) {
|
||||
// Single-letter variable names are used to save characters (code golfing).
|
||||
vec3 p // The current point in 3D space along the ray.
|
||||
, O // Multi-purpose vector: color accumulator, then normal vector, then final color.
|
||||
, r=vec3(iResolution.xy, iResolution.y) // 'r' holds screen resolution, later re-used for the epsilon vector and reflection.
|
||||
// 'I' is the Ray Direction vector. It's calculated once per pixel.
|
||||
// This converts the 2D screen coordinate 'C' into a 3D direction, creating the camera perspective.
|
||||
, I=normalize(vec3(C-.5*r.xy, r.y))
|
||||
// Base glow color (dark bluish tint).
|
||||
, B=vec3(1,2,9)*M
|
||||
;
|
||||
|
||||
// == Raymarching Loop ==
|
||||
// This loop "marches" a ray from the camera out into the scene to find what it hits.
|
||||
// It uses a golfed structure where the body of the loop updates the ray position 'p',
|
||||
// and the "advancement" step moves the ray forward.
|
||||
for(
|
||||
// -- Initializer (runs once before the loop) --
|
||||
// Calculate the rotation matrix for this frame based on time.
|
||||
R = mat2(cos(.3*iTime+vec4(0,11,33,0)))
|
||||
// -- Condition --
|
||||
// Loop while total distance 'z' is less than 9 and we are not yet touching a surface (d > 1e-3).
|
||||
; z<9. && d > M
|
||||
// -- Advancement --
|
||||
// The ray advances by the safe distance 'd' returned by D(p).
|
||||
// The result of D(p) is also assigned to the global 'd' inside the function.
|
||||
; z += D(p)
|
||||
)
|
||||
// -- Loop Body --
|
||||
// Calculate the current position 'p' in world space.
|
||||
// The camera starts at (0,0,-2) and points forward.
|
||||
p = z*I
|
||||
, p.z -= 2.
|
||||
;
|
||||
|
||||
// -- Hit Condition --
|
||||
// If the loop finished because z exceeded the max distance, we hit nothing. Otherwise, we hit the surface.
|
||||
if (z < 9.) {
|
||||
// -- Calculate Surface Normal --
|
||||
// Estimate the gradient ∇D at the hit point 'p' via central differences on the SDF D.
|
||||
// We use ε = 1e-3 and loop over each axis (x, y, z):
|
||||
// • Zero r, then set r[i] = ε.
|
||||
// • Compute O[i] = D(p + r) – D(p – r).
|
||||
// After the loop, O holds the unnormalized normal vector.
|
||||
for (
|
||||
int i=0 // axis index: 0→x, 1→y, 2→z (initialized to avoid warnings)
|
||||
; i < 3
|
||||
; O[i++] = D(p+r) - D(p-r)
|
||||
)
|
||||
r -= r // clear r to vec3(0)
|
||||
, r[i] = M // set only the i-th component
|
||||
;
|
||||
|
||||
// -- Lighting and Shading --
|
||||
// 'z' is re-purposed to store a fresnel factor (1 - cos(angle)) for edge brightness.
|
||||
// `dot(O, I)` calculates how much the surface faces away from the camera.
|
||||
// O is also normalized here to become a proper normal vector.
|
||||
z = 1.+dot(O = normalize(O),I);
|
||||
|
||||
// 'r' is re-purposed to store the reflection vector.
|
||||
r = reflect(I,O);
|
||||
|
||||
// Calculate a point 'C' along the reflection vector 'r' to sample a background color.
|
||||
// For upward reflections (r.y > 0), this finds the intersection with the plane y=5.
|
||||
C = (p+r*(5.-p.y)/abs(r.y)).xz;
|
||||
|
||||
// Calculate the final color 'O' of the hit point.
|
||||
O =
|
||||
// Multiply by the fresnel factor squared for stronger edge reflections.
|
||||
z*z *
|
||||
// Use a ternary operator to decide the color based on where the reflection ray goes.
|
||||
(
|
||||
// If the reflection vector points upward...
|
||||
r.y>0.
|
||||
// ...sample a procedural "sky" with a radial gradient and blue tint.
|
||||
? 5e2*smoothstep(5., 4., d = sqrt(length(C*C))+1.)*d*B
|
||||
// ...otherwise, sample a "floor" with a deep blue exponential falloff.
|
||||
: exp(-2.*length(C))*(B/M-1.)
|
||||
)
|
||||
// Add rim lighting (brighter on upward-facing surfaces).
|
||||
+ pow(1.+O.y,5.)*B
|
||||
;
|
||||
}
|
||||
|
||||
// == Tonemapping & Output ==
|
||||
// Apply final effects and map the High Dynamic Range (HDR) color to a displayable range.
|
||||
// Add glow contribution: smaller G values (closer ray passes) create a brighter blue glow.
|
||||
o = sqrt(O+B/G).xyzx;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name: Octograms
|
||||
// Author: whisky_shusuky
|
||||
// URL: https://www.shadertoy.com/view/tlVGDt
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
|
||||
73
shaders/remember.frag.glsl
Normal file
73
shaders/remember.frag.glsl
Normal file
@@ -0,0 +1,73 @@
|
||||
// Name: Remember
|
||||
// Author: diatribes
|
||||
// URL: https://www.shadertoy.com/view/tXSBDK
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
// fuzzy brain
|
||||
|
||||
// Hash function to replace iChannel0 texture noise
|
||||
float hash12(vec2 p) {
|
||||
vec3 p3 = fract(vec3(p.xyx) * .1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
void mainImage(out vec4 o, vec2 u) {
|
||||
|
||||
vec3 q,p = vec3(iResolution.xy, iResolution.x / iResolution.y);
|
||||
|
||||
float i = 0.0, s,
|
||||
// start the ray at a small random distance,
|
||||
// this will reduce banding
|
||||
// Replaced texelFetch(iChannel0, ...) with hash function
|
||||
d = .125 * hash12(u),
|
||||
t = iTime * .1;
|
||||
|
||||
// scale coords
|
||||
u = (u+u-p.xy)/p.y;
|
||||
if (abs(u.y) > .8) { o = vec4(0); return; }
|
||||
|
||||
// Initialize output color (out parameter must be initialized before use)
|
||||
o = vec4(0.0);
|
||||
|
||||
for(; i<64.; i++) {
|
||||
|
||||
// shorthand for standard raymarch sample, then move forward:
|
||||
// p = ro + rd * d, p.z + t
|
||||
q = p = vec3(u * d, d + t*5.);
|
||||
p.xy *= mat2(cos(.1*p.z+.1*t+vec4(0,33,11,0)));
|
||||
|
||||
q.xz = cos(q.xz);
|
||||
p.z = cos(p.z) ;
|
||||
// turbulence
|
||||
for (s = 1.; s++ <6.;
|
||||
q += sin(.6*t+p.zxy*.6),
|
||||
p += sin(t+t+p.yzx*s)*.6);
|
||||
|
||||
// distance to spheres
|
||||
d += s = .02 + abs(min(length(p+3.*sin(p.z*.5))-4., length(q-2.*sin(p.z*.4))-6.))*.2;
|
||||
|
||||
// color: 1.+cos so we don't go negative, cos(d+vec4(6,4,2,0)) samples from the palette
|
||||
// divide by s for form and distance
|
||||
// Clamp only the first term to prevent extreme overflow, leave second term free
|
||||
vec4 brightTerm = min(.01*vec4(6,2,1,0)/max(length(u*sin(t+t+t)), 0.001), vec4(50.0));
|
||||
o += brightTerm + 1. / s * length(u);
|
||||
|
||||
}
|
||||
|
||||
// tonemap and divide brightness
|
||||
o = tanh(max(o /6e2 + dot(u,u)*.35, 0.));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
223
shaders/seascape.frag.glsl
Normal file
223
shaders/seascape.frag.glsl
Normal file
@@ -0,0 +1,223 @@
|
||||
// Name: Seascape
|
||||
// Author: Alexander Alekseev
|
||||
// URL: https://www.shadertoy.com/view/Ms2SD1
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
/*
|
||||
* "Seascape" by Alexander Alekseev aka TDM - 2014
|
||||
* License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
* Contact: tdmaav@gmail.com
|
||||
*/
|
||||
|
||||
const int NUM_STEPS = 32;
|
||||
const float PI = 3.141592;
|
||||
const float EPSILON = 1e-3;
|
||||
#define EPSILON_NRM (0.1 / iResolution.x)
|
||||
//#define AA
|
||||
|
||||
// sea
|
||||
const int ITER_GEOMETRY = 3;
|
||||
const int ITER_FRAGMENT = 5;
|
||||
const float SEA_HEIGHT = 0.6;
|
||||
const float SEA_CHOPPY = 4.0;
|
||||
const float SEA_SPEED = 0.8;
|
||||
const float SEA_FREQ = 0.16;
|
||||
const vec3 SEA_BASE = vec3(0.0,0.09,0.18);
|
||||
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6)*0.6;
|
||||
#define SEA_TIME (1.0 + iTime * SEA_SPEED)
|
||||
const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
|
||||
|
||||
// math
|
||||
mat3 fromEuler(vec3 ang) {
|
||||
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
|
||||
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
|
||||
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
|
||||
mat3 m;
|
||||
m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
|
||||
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
|
||||
m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
|
||||
return m;
|
||||
}
|
||||
float hash( vec2 p ) {
|
||||
float h = dot(p,vec2(127.1,311.7));
|
||||
return fract(sin(h)*43758.5453123);
|
||||
}
|
||||
float noise( in vec2 p ) {
|
||||
vec2 i = floor( p );
|
||||
vec2 f = fract( p );
|
||||
vec2 u = f*f*(3.0-2.0*f);
|
||||
return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),
|
||||
hash( i + vec2(1.0,0.0) ), u.x),
|
||||
mix( hash( i + vec2(0.0,1.0) ),
|
||||
hash( i + vec2(1.0,1.0) ), u.x), u.y);
|
||||
}
|
||||
|
||||
// lighting
|
||||
float diffuse(vec3 n,vec3 l,float p) {
|
||||
return pow(dot(n,l) * 0.4 + 0.6,p);
|
||||
}
|
||||
float specular(vec3 n,vec3 l,vec3 e,float s) {
|
||||
float nrm = (s + 8.0) / (PI * 8.0);
|
||||
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
|
||||
}
|
||||
|
||||
// sky
|
||||
vec3 getSkyColor(vec3 e) {
|
||||
e.y = (max(e.y,0.0)*0.8+0.2)*0.8;
|
||||
return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1;
|
||||
}
|
||||
|
||||
// sea
|
||||
float sea_octave(vec2 uv, float choppy) {
|
||||
uv += noise(uv);
|
||||
vec2 wv = 1.0-abs(sin(uv));
|
||||
vec2 swv = abs(cos(uv));
|
||||
wv = mix(wv,swv,wv);
|
||||
return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
|
||||
}
|
||||
|
||||
float map(vec3 p) {
|
||||
float freq = SEA_FREQ;
|
||||
float amp = SEA_HEIGHT;
|
||||
float choppy = SEA_CHOPPY;
|
||||
vec2 uv = p.xz; uv.x *= 0.75;
|
||||
|
||||
float d, h = 0.0;
|
||||
for(int i = 0; i < ITER_GEOMETRY; i++) {
|
||||
d = sea_octave((uv+SEA_TIME)*freq,choppy);
|
||||
d += sea_octave((uv-SEA_TIME)*freq,choppy);
|
||||
h += d * amp;
|
||||
uv *= octave_m; freq *= 1.9; amp *= 0.22;
|
||||
choppy = mix(choppy,1.0,0.2);
|
||||
}
|
||||
return p.y - h;
|
||||
}
|
||||
|
||||
float map_detailed(vec3 p) {
|
||||
float freq = SEA_FREQ;
|
||||
float amp = SEA_HEIGHT;
|
||||
float choppy = SEA_CHOPPY;
|
||||
vec2 uv = p.xz; uv.x *= 0.75;
|
||||
|
||||
float d, h = 0.0;
|
||||
for(int i = 0; i < ITER_FRAGMENT; i++) {
|
||||
d = sea_octave((uv+SEA_TIME)*freq,choppy);
|
||||
d += sea_octave((uv-SEA_TIME)*freq,choppy);
|
||||
h += d * amp;
|
||||
uv *= octave_m; freq *= 1.9; amp *= 0.22;
|
||||
choppy = mix(choppy,1.0,0.2);
|
||||
}
|
||||
return p.y - h;
|
||||
}
|
||||
|
||||
vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
|
||||
float fresnel = clamp(1.0 - dot(n, -eye), 0.0, 1.0);
|
||||
fresnel = min(fresnel * fresnel * fresnel, 0.5);
|
||||
|
||||
vec3 reflected = getSkyColor(reflect(eye, n));
|
||||
vec3 refracted = SEA_BASE + diffuse(n, l, 80.0) * SEA_WATER_COLOR * 0.12;
|
||||
|
||||
vec3 color = mix(refracted, reflected, fresnel);
|
||||
|
||||
float atten = max(1.0 - dot(dist, dist) * 0.001, 0.0);
|
||||
color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
|
||||
|
||||
color += specular(n, l, eye, 600.0 * inversesqrt(dot(dist,dist)));
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
// tracing
|
||||
vec3 getNormal(vec3 p, float eps) {
|
||||
vec3 n;
|
||||
n.y = map_detailed(p);
|
||||
n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
|
||||
n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
|
||||
n.y = eps;
|
||||
return normalize(n);
|
||||
}
|
||||
|
||||
float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
|
||||
float tm = 0.0;
|
||||
float tx = 1000.0;
|
||||
float hx = map(ori + dir * tx);
|
||||
if(hx > 0.0) {
|
||||
p = ori + dir * tx;
|
||||
return tx;
|
||||
}
|
||||
float hm = map(ori);
|
||||
for(int i = 0; i < NUM_STEPS; i++) {
|
||||
float tmid = mix(tm, tx, hm / (hm - hx));
|
||||
p = ori + dir * tmid;
|
||||
float hmid = map(p);
|
||||
if(hmid < 0.0) {
|
||||
tx = tmid;
|
||||
hx = hmid;
|
||||
} else {
|
||||
tm = tmid;
|
||||
hm = hmid;
|
||||
}
|
||||
if(abs(hmid) < EPSILON) break;
|
||||
}
|
||||
return mix(tm, tx, hm / (hm - hx));
|
||||
}
|
||||
|
||||
vec3 getPixel(in vec2 coord, float time) {
|
||||
vec2 uv = coord / iResolution.xy;
|
||||
uv = uv * 2.0 - 1.0;
|
||||
uv.x *= iResolution.x / iResolution.y;
|
||||
|
||||
// ray
|
||||
vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);
|
||||
vec3 ori = vec3(0.0,3.5,time*5.0);
|
||||
vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.14;
|
||||
dir = normalize(dir) * fromEuler(ang);
|
||||
|
||||
// tracing
|
||||
vec3 p;
|
||||
heightMapTracing(ori,dir,p);
|
||||
vec3 dist = p - ori;
|
||||
vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
|
||||
vec3 light = normalize(vec3(0.0,1.0,0.8));
|
||||
|
||||
// color
|
||||
return mix(
|
||||
getSkyColor(dir),
|
||||
getSeaColor(p,n,light,dir,dist),
|
||||
pow(smoothstep(0.0,-0.02,dir.y),0.2));
|
||||
}
|
||||
|
||||
// main
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
|
||||
// Removed mouse interaction (iMouse not available)
|
||||
float time = iTime * 0.3;
|
||||
|
||||
#ifdef AA
|
||||
vec3 color = vec3(0.0);
|
||||
for(int i = -1; i <= 1; i++) {
|
||||
for(int j = -1; j <= 1; j++) {
|
||||
vec2 uv = fragCoord+vec2(i,j)/3.0;
|
||||
color += getPixel(uv, time);
|
||||
}
|
||||
}
|
||||
color /= 9.0;
|
||||
#else
|
||||
vec3 color = getPixel(fragCoord, time);
|
||||
#endif
|
||||
|
||||
// post
|
||||
fragColor = vec4(pow(color,vec3(0.65)), 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Name: Shader Art Coding Introduction
|
||||
// Author: kishimisu
|
||||
// URL: https://www.shadertoy.com/view/mtyGWy
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Name: Test
|
||||
// Author: JailDesigner
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
|
||||
44
shaders/water.glsl
Normal file
44
shaders/water.glsl
Normal file
@@ -0,0 +1,44 @@
|
||||
// Name: Water
|
||||
// Author: diatribes
|
||||
// URL: https://www.shadertoy.com/view/tXjXDy
|
||||
#version 330 core
|
||||
precision highp float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 vUV;
|
||||
uniform vec2 iResolution;
|
||||
uniform float iTime;
|
||||
|
||||
/*
|
||||
-2 by @FabriceNeyret2
|
||||
|
||||
thanks!! :D
|
||||
|
||||
If it doesn't display correctly, change line 17 "r/r" to "vec3(1)"
|
||||
*/
|
||||
|
||||
void mainImage( out vec4 o, vec2 u ) {
|
||||
float s=.002, i=0., n; // FIXED: Initialize i=0
|
||||
vec3 r = vec3(iResolution.xy, iResolution.x/iResolution.y);
|
||||
vec3 p = vec3(0);
|
||||
u = (u-r.xy/2.)/r.y-.3;
|
||||
|
||||
o = vec4(0); // FIXED: Initialize output to black
|
||||
|
||||
for(; i < 32. && s > .001; i++) {
|
||||
// Clamp only extreme overflow values, let normal brightness through
|
||||
vec4 term = vec4(5,2,1,0)/max(length(u-.1), 0.001);
|
||||
o += min(term, vec4(100.0));
|
||||
for (p += vec3(u*s,s), s = 1. + p.y, n =.01; n < 1.; n+=n) {
|
||||
s += abs(dot(sin(p.z+iTime+p / n), vec3(1))) * n*.1;
|
||||
}
|
||||
}
|
||||
o = tanh(o/5e2);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 fragCoordPixels = vUV * iResolution;
|
||||
vec4 outColor;
|
||||
mainImage(outColor, fragCoordPixels);
|
||||
FragColor = outColor;
|
||||
}
|
||||
610
src/main.cpp
610
src/main.cpp
@@ -7,8 +7,10 @@
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <filesystem>
|
||||
#include <ctime>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <glad/glad.h>
|
||||
#include "jail_audio.h"
|
||||
|
||||
#include "defines.hpp"
|
||||
|
||||
@@ -21,6 +23,7 @@ struct Logger {
|
||||
// Opciones mínimas parecidas a las tuyas
|
||||
struct VideoOptions {
|
||||
bool fullscreen = false;
|
||||
bool vsync = true;
|
||||
} Options_video;
|
||||
|
||||
// Estructura para guardar info del display
|
||||
@@ -31,17 +34,66 @@ struct DisplayMonitor {
|
||||
int refresh_rate = 0;
|
||||
};
|
||||
|
||||
// Forward declarations of structs
|
||||
struct ShaderMetadata {
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::string iChannel0; // "BufferA", "BufferB", "none", etc.
|
||||
std::string iChannel1;
|
||||
std::string iChannel2;
|
||||
std::string iChannel3;
|
||||
};
|
||||
|
||||
struct ShaderBuffer {
|
||||
GLuint program = 0; // Shader program for this buffer
|
||||
GLuint fbo = 0; // Framebuffer object
|
||||
GLuint texture = 0; // Output texture
|
||||
std::string name; // "BufferA", "BufferB", etc.
|
||||
};
|
||||
|
||||
struct ShaderPass {
|
||||
std::string shaderName; // Base name (e.g., "water")
|
||||
std::string displayName; // Custom name from metadata
|
||||
std::string author; // Author from metadata
|
||||
GLuint imageProgram = 0; // Main image shader program
|
||||
std::vector<ShaderBuffer> buffers; // BufferA, BufferB, etc.
|
||||
ShaderMetadata metadata; // iChannel configuration
|
||||
};
|
||||
|
||||
// Globales simplificados (tu proyecto puede integrarlo en clases)
|
||||
static DisplayMonitor display_monitor_;
|
||||
static SDL_Window* window_ = nullptr;
|
||||
|
||||
// Sistema de shaders
|
||||
// Sistema de shaders (legacy - kept for backward compatibility with single-pass shaders)
|
||||
static std::vector<std::filesystem::path> shader_list_;
|
||||
static std::vector<std::string> shader_names_; // Custom names from "// Name: XXX" comments
|
||||
static std::vector<std::string> shader_authors_; // Custom authors from "// Author: XXX" comments
|
||||
static size_t current_shader_index_ = 0;
|
||||
static std::filesystem::path shaders_directory_;
|
||||
static GLuint current_program_ = 0;
|
||||
static Uint32 shader_start_ticks_ = 0;
|
||||
|
||||
// Multi-pass shader system
|
||||
static std::vector<ShaderPass> shader_passes_;
|
||||
static int current_window_width_ = 0;
|
||||
static int current_window_height_ = 0;
|
||||
|
||||
// Self-feedback system (for shaders that use their own output as input)
|
||||
static GLuint feedback_fbo_ = 0;
|
||||
static GLuint feedback_texture_ = 0;
|
||||
static bool current_shader_uses_feedback_ = false;
|
||||
static int feedback_channel_ = -1; // Which iChannel (0-3) is used for feedback
|
||||
|
||||
// FPS tracking
|
||||
static Uint32 fps_frame_count_ = 0;
|
||||
static Uint32 fps_last_update_ticks_ = 0;
|
||||
static float current_fps_ = 0.0f;
|
||||
|
||||
// Sistema de música
|
||||
static std::vector<std::filesystem::path> music_list_;
|
||||
static size_t current_music_index_ = 0;
|
||||
static JA_Music_t* current_music_ = nullptr;
|
||||
|
||||
// Vertex shader embebido
|
||||
static const char* vertexShaderSrc = R"glsl(
|
||||
#version 330 core
|
||||
@@ -63,6 +115,64 @@ static bool loadFileToString(const std::filesystem::path& path, std::string& out
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string trimString(const std::string& str) {
|
||||
size_t start = str.find_first_not_of(" \t\r\n");
|
||||
size_t end = str.find_last_not_of(" \t\r\n");
|
||||
if (start != std::string::npos && end != std::string::npos) {
|
||||
return str.substr(start, end - start + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static ShaderMetadata extractShaderMetadata(const std::string& shaderSource) {
|
||||
ShaderMetadata metadata;
|
||||
metadata.iChannel0 = "none";
|
||||
metadata.iChannel1 = "none";
|
||||
metadata.iChannel2 = "none";
|
||||
metadata.iChannel3 = "none";
|
||||
|
||||
std::istringstream stream(shaderSource);
|
||||
std::string line;
|
||||
int lineCount = 0;
|
||||
const int maxLinesToCheck = 30;
|
||||
|
||||
while (std::getline(stream, line) && lineCount < maxLinesToCheck) {
|
||||
lineCount++;
|
||||
|
||||
// Look for "// XXX: YYY" patterns (case-insensitive)
|
||||
size_t pos = line.find("//");
|
||||
if (pos != std::string::npos) {
|
||||
std::string comment = line.substr(pos + 2);
|
||||
std::string commentLower = comment;
|
||||
std::transform(commentLower.begin(), commentLower.end(), commentLower.begin(), ::tolower);
|
||||
|
||||
// Check for Name:
|
||||
if (commentLower.find("name:") != std::string::npos) {
|
||||
metadata.name = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
// Check for Author:
|
||||
else if (commentLower.find("author:") != std::string::npos) {
|
||||
metadata.author = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
// Check for iChannel0-3:
|
||||
else if (commentLower.find("ichannel0:") != std::string::npos) {
|
||||
metadata.iChannel0 = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
else if (commentLower.find("ichannel1:") != std::string::npos) {
|
||||
metadata.iChannel1 = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
else if (commentLower.find("ichannel2:") != std::string::npos) {
|
||||
metadata.iChannel2 = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
else if (commentLower.find("ichannel3:") != std::string::npos) {
|
||||
metadata.iChannel3 = trimString(comment.substr(comment.find(":") + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> scanShaderDirectory(const std::filesystem::path& directory) {
|
||||
std::vector<std::filesystem::path> shaders;
|
||||
|
||||
@@ -84,14 +194,333 @@ static std::vector<std::filesystem::path> scanShaderDirectory(const std::filesys
|
||||
std::sort(shaders.begin(), shaders.end());
|
||||
|
||||
Logger::info("Found " + std::to_string(shaders.size()) + " shader(s) in " + directory.string());
|
||||
|
||||
// Initialize shader metadata vectors with empty strings (will be filled when shaders are loaded)
|
||||
shader_names_.resize(shaders.size(), "");
|
||||
shader_authors_.resize(shaders.size(), "");
|
||||
|
||||
return shaders;
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> scanMusicDirectory(const std::filesystem::path& directory) {
|
||||
std::vector<std::filesystem::path> music_files;
|
||||
|
||||
if (!std::filesystem::exists(directory) || !std::filesystem::is_directory(directory)) {
|
||||
Logger::info("Music directory does not exist: " + directory.string());
|
||||
return music_files;
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
|
||||
if (entry.is_regular_file()) {
|
||||
auto ext = entry.path().extension().string();
|
||||
if (ext == ".ogg") {
|
||||
music_files.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ordenar alfabéticamente
|
||||
std::sort(music_files.begin(), music_files.end());
|
||||
|
||||
Logger::info("Found " + std::to_string(music_files.size()) + " music file(s) in " + directory.string());
|
||||
return music_files;
|
||||
}
|
||||
|
||||
static void playRandomMusic() {
|
||||
if (music_list_.empty()) return;
|
||||
|
||||
// Liberar música anterior si existe
|
||||
if (current_music_) {
|
||||
JA_DeleteMusic(current_music_);
|
||||
current_music_ = nullptr;
|
||||
}
|
||||
|
||||
// Elegir índice aleatorio
|
||||
current_music_index_ = rand() % music_list_.size();
|
||||
|
||||
// Cargar y reproducir música (sin loop, loop=0)
|
||||
const auto& music_path = music_list_[current_music_index_];
|
||||
current_music_ = JA_LoadMusic(music_path.string().c_str());
|
||||
|
||||
if (current_music_) {
|
||||
JA_PlayMusic(current_music_, 0); // 0 = no loop, se reproduce una vez
|
||||
Logger::info("Now playing: " + music_path.filename().string());
|
||||
} else {
|
||||
Logger::error("Failed to load music: " + music_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Multi-pass FBO/Texture Management =====
|
||||
|
||||
static bool createBufferFBO(ShaderBuffer& buffer, int width, int height) {
|
||||
// Create texture
|
||||
glGenTextures(1, &buffer.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, buffer.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Create FBO
|
||||
glGenFramebuffers(1, &buffer.fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, buffer.fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, buffer.texture, 0);
|
||||
|
||||
// Check FBO completeness
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Logger::error("FBO creation failed for " + buffer.name + ": " + std::to_string(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::info("Created FBO for " + buffer.name + " (" + std::to_string(width) + "x" + std::to_string(height) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroyBuffer(ShaderBuffer& buffer) {
|
||||
if (buffer.fbo != 0) {
|
||||
glDeleteFramebuffers(1, &buffer.fbo);
|
||||
buffer.fbo = 0;
|
||||
}
|
||||
if (buffer.texture != 0) {
|
||||
glDeleteTextures(1, &buffer.texture);
|
||||
buffer.texture = 0;
|
||||
}
|
||||
if (buffer.program != 0) {
|
||||
glDeleteProgram(buffer.program);
|
||||
buffer.program = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void destroyShaderPass(ShaderPass& pass) {
|
||||
if (pass.imageProgram != 0) {
|
||||
glDeleteProgram(pass.imageProgram);
|
||||
pass.imageProgram = 0;
|
||||
}
|
||||
for (auto& buffer : pass.buffers) {
|
||||
destroyBuffer(buffer);
|
||||
}
|
||||
pass.buffers.clear();
|
||||
}
|
||||
|
||||
static bool resizeBuffersIfNeeded(ShaderPass& pass, int width, int height) {
|
||||
if (current_window_width_ == width && current_window_height_ == height) {
|
||||
return false; // No resize needed
|
||||
}
|
||||
|
||||
Logger::info("Resizing buffers: " + std::to_string(width) + "x" + std::to_string(height));
|
||||
|
||||
// Destroy and recreate all buffers with new size
|
||||
for (auto& buffer : pass.buffers) {
|
||||
// Keep program, destroy FBO/texture only
|
||||
if (buffer.fbo != 0) glDeleteFramebuffers(1, &buffer.fbo);
|
||||
if (buffer.texture != 0) glDeleteTextures(1, &buffer.texture);
|
||||
buffer.fbo = 0;
|
||||
buffer.texture = 0;
|
||||
|
||||
if (!createBufferFBO(buffer, width, height)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
current_window_width_ = width;
|
||||
current_window_height_ = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===== Self-Feedback System =====
|
||||
|
||||
static bool createFeedbackFBO(int width, int height) {
|
||||
// Delete existing if any
|
||||
if (feedback_fbo_ != 0) glDeleteFramebuffers(1, &feedback_fbo_);
|
||||
if (feedback_texture_ != 0) glDeleteTextures(1, &feedback_texture_);
|
||||
|
||||
// Create texture
|
||||
glGenTextures(1, &feedback_texture_);
|
||||
glBindTexture(GL_TEXTURE_2D, feedback_texture_);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Clear to black initially
|
||||
std::vector<float> black(width * height * 4, 0.0f);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, black.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Create FBO
|
||||
glGenFramebuffers(1, &feedback_fbo_);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, feedback_texture_, 0);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Logger::error("Feedback FBO creation failed: " + std::to_string(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::info("Created feedback FBO (" + std::to_string(width) + "x" + std::to_string(height) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroyFeedbackFBO() {
|
||||
if (feedback_fbo_ != 0) {
|
||||
glDeleteFramebuffers(1, &feedback_fbo_);
|
||||
feedback_fbo_ = 0;
|
||||
}
|
||||
if (feedback_texture_ != 0) {
|
||||
glDeleteTextures(1, &feedback_texture_);
|
||||
feedback_texture_ = 0;
|
||||
}
|
||||
current_shader_uses_feedback_ = false;
|
||||
feedback_channel_ = -1;
|
||||
}
|
||||
|
||||
static int detectFeedbackChannel(const ShaderMetadata& metadata) {
|
||||
// Check which iChannel uses "self" feedback
|
||||
if (metadata.iChannel0 == "self") return 0;
|
||||
if (metadata.iChannel1 == "self") return 1;
|
||||
if (metadata.iChannel2 == "self") return 2;
|
||||
if (metadata.iChannel3 == "self") return 3;
|
||||
return -1; // No feedback
|
||||
}
|
||||
|
||||
// ===== Multi-pass Shader Loading =====
|
||||
|
||||
// Forward declarations
|
||||
static GLuint compileShader(GLenum type, const char* src);
|
||||
static GLuint linkProgram(GLuint vs, GLuint fs);
|
||||
|
||||
static std::vector<std::string> findBufferFiles(const std::filesystem::path& imagePath) {
|
||||
std::vector<std::string> buffers;
|
||||
std::filesystem::path dir = imagePath.parent_path();
|
||||
std::string basename = imagePath.stem().stem().string(); // Remove .image.glsl -> get base name
|
||||
|
||||
// Check for BufferA, BufferB, BufferC, BufferD
|
||||
std::vector<std::string> bufferNames = {"bufferA", "bufferB", "bufferC", "bufferD"};
|
||||
for (const auto& bufName : bufferNames) {
|
||||
std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl");
|
||||
if (std::filesystem::exists(bufferPath)) {
|
||||
buffers.push_back(bufName);
|
||||
Logger::info("Found buffer: " + bufferPath.string());
|
||||
}
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
static GLuint loadMultiPassShader(const std::filesystem::path& imagePath, ShaderPass& outPass, int width, int height) {
|
||||
std::string basename = imagePath.stem().stem().string();
|
||||
outPass.shaderName = basename;
|
||||
|
||||
// Load and compile Image shader
|
||||
std::string imageSrc;
|
||||
if (!loadFileToString(imagePath, imageSrc)) {
|
||||
Logger::error("Failed to load image shader: " + imagePath.string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract metadata from Image shader
|
||||
outPass.metadata = extractShaderMetadata(imageSrc);
|
||||
outPass.displayName = outPass.metadata.name.empty() ? basename : outPass.metadata.name;
|
||||
outPass.author = outPass.metadata.author;
|
||||
|
||||
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, imageSrc.c_str());
|
||||
|
||||
if (!vs || !fs) {
|
||||
if (vs) glDeleteShader(vs);
|
||||
if (fs) glDeleteShader(fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
outPass.imageProgram = linkProgram(vs, fs);
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
|
||||
if (!outPass.imageProgram) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find and load buffer shaders
|
||||
std::vector<std::string> bufferNames = findBufferFiles(imagePath);
|
||||
std::filesystem::path dir = imagePath.parent_path();
|
||||
|
||||
for (const auto& bufName : bufferNames) {
|
||||
ShaderBuffer buffer;
|
||||
buffer.name = bufName;
|
||||
|
||||
// Load buffer shader source
|
||||
std::filesystem::path bufferPath = dir / (basename + "." + bufName + ".glsl");
|
||||
std::string bufferSrc;
|
||||
if (!loadFileToString(bufferPath, bufferSrc)) {
|
||||
Logger::error("Failed to load buffer: " + bufferPath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compile buffer shader
|
||||
GLuint bufVs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||
GLuint bufFs = compileShader(GL_FRAGMENT_SHADER, bufferSrc.c_str());
|
||||
|
||||
if (!bufVs || !bufFs) {
|
||||
if (bufVs) glDeleteShader(bufVs);
|
||||
if (bufFs) glDeleteShader(bufFs);
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer.program = linkProgram(bufVs, bufFs);
|
||||
glDeleteShader(bufVs);
|
||||
glDeleteShader(bufFs);
|
||||
|
||||
if (!buffer.program) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create FBO and texture for this buffer
|
||||
if (!createBufferFBO(buffer, width, height)) {
|
||||
glDeleteProgram(buffer.program);
|
||||
continue;
|
||||
}
|
||||
|
||||
outPass.buffers.push_back(buffer);
|
||||
Logger::info("Loaded buffer: " + bufName);
|
||||
}
|
||||
|
||||
Logger::info("Multi-pass shader loaded: " + outPass.displayName + " (" + std::to_string(outPass.buffers.size()) + " buffers)");
|
||||
return outPass.imageProgram;
|
||||
}
|
||||
|
||||
static void updateWindowTitle() {
|
||||
if (!window_ || shader_list_.empty()) return;
|
||||
|
||||
std::string filename = shader_list_[current_shader_index_].filename().string();
|
||||
std::string title = std::string(APP_NAME) + " (" + filename + ")";
|
||||
// Use custom shader name if available, otherwise fallback to filename
|
||||
std::string shaderName;
|
||||
if (!shader_names_.empty() && !shader_names_[current_shader_index_].empty()) {
|
||||
shaderName = shader_names_[current_shader_index_];
|
||||
} else {
|
||||
shaderName = shader_list_[current_shader_index_].filename().string();
|
||||
}
|
||||
|
||||
// Add author if available
|
||||
if (!shader_authors_.empty() && !shader_authors_[current_shader_index_].empty()) {
|
||||
shaderName += " by " + shader_authors_[current_shader_index_];
|
||||
}
|
||||
|
||||
std::string title = std::string(APP_NAME) + " (" + shaderName + ")";
|
||||
|
||||
if (current_fps_ > 0.0f) {
|
||||
title += " - " + std::to_string(static_cast<int>(current_fps_ + 0.5f)) + " FPS";
|
||||
}
|
||||
|
||||
title += Options_video.vsync ? " [VSync ON]" : " [VSync OFF]";
|
||||
|
||||
SDL_SetWindowTitle(window_, title.c_str());
|
||||
}
|
||||
|
||||
@@ -147,6 +576,25 @@ static GLuint loadAndCompileShader(size_t index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract custom shader metadata (name, author, iChannels) from source code
|
||||
ShaderMetadata metadata = extractShaderMetadata(fragSrc);
|
||||
if (!metadata.name.empty()) {
|
||||
shader_names_[index] = metadata.name;
|
||||
Logger::info("Shader name: " + metadata.name);
|
||||
}
|
||||
if (!metadata.author.empty()) {
|
||||
shader_authors_[index] = metadata.author;
|
||||
Logger::info("Shader author: " + metadata.author);
|
||||
}
|
||||
|
||||
// Detect self-feedback
|
||||
feedback_channel_ = detectFeedbackChannel(metadata);
|
||||
current_shader_uses_feedback_ = (feedback_channel_ >= 0);
|
||||
|
||||
if (current_shader_uses_feedback_) {
|
||||
Logger::info("Shader uses self-feedback on iChannel" + std::to_string(feedback_channel_));
|
||||
}
|
||||
|
||||
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
|
||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragSrc.c_str());
|
||||
|
||||
@@ -208,11 +656,15 @@ void setFullscreenMode() {
|
||||
SDL_SetWindowFullscreen(window_, false);
|
||||
SDL_SetWindowSize(window_, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
Options_video.fullscreen = false;
|
||||
SDL_ShowCursor(); // Show cursor on fallback to windowed
|
||||
} else {
|
||||
SDL_HideCursor(); // Hide cursor in fullscreen
|
||||
}
|
||||
} else {
|
||||
// Volver a modo ventana 800x800
|
||||
SDL_SetWindowFullscreen(window_, false);
|
||||
SDL_SetWindowSize(window_, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
SDL_ShowCursor(); // Show cursor in windowed mode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,6 +673,17 @@ void toggleFullscreen() {
|
||||
setFullscreenMode();
|
||||
}
|
||||
|
||||
void toggleVSync() {
|
||||
Options_video.vsync = !Options_video.vsync;
|
||||
int result = SDL_GL_SetSwapInterval(Options_video.vsync ? 1 : 0);
|
||||
|
||||
if (result == 0) {
|
||||
Logger::info(Options_video.vsync ? "VSync enabled" : "VSync disabled");
|
||||
} else {
|
||||
Logger::error(std::string("Failed to set VSync: ") + SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
void switchShader(int direction) {
|
||||
if (shader_list_.empty()) return;
|
||||
|
||||
@@ -244,6 +707,9 @@ void switchShader(int direction) {
|
||||
glDeleteProgram(current_program_);
|
||||
}
|
||||
|
||||
// Destroy feedback FBO from previous shader
|
||||
destroyFeedbackFBO();
|
||||
|
||||
current_program_ = new_program;
|
||||
current_shader_index_ = new_index;
|
||||
shader_start_ticks_ = SDL_GetTicks();
|
||||
@@ -260,6 +726,10 @@ void handleDebugEvents(const SDL_Event& event) {
|
||||
toggleFullscreen();
|
||||
break;
|
||||
}
|
||||
case SDLK_F4: {
|
||||
toggleVSync();
|
||||
break;
|
||||
}
|
||||
case SDLK_LEFT: {
|
||||
switchShader(-1);
|
||||
break;
|
||||
@@ -287,7 +757,7 @@ int main(int argc, char** argv) {
|
||||
Options_video.fullscreen = fullscreenFlag;
|
||||
|
||||
// Inicializar SDL3
|
||||
auto initResult = SDL_Init(SDL_INIT_VIDEO);
|
||||
auto initResult = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
if constexpr (std::is_same_v<decltype(initResult), bool>) {
|
||||
if (!initResult) { Logger::error(SDL_GetError()); return -1; }
|
||||
} else {
|
||||
@@ -326,9 +796,34 @@ int main(int argc, char** argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set initial vsync state
|
||||
int vsync_result = SDL_GL_SetSwapInterval(Options_video.vsync ? 1 : 0);
|
||||
if (vsync_result == 0) {
|
||||
Logger::info(Options_video.vsync ? "VSync enabled" : "VSync disabled");
|
||||
} else {
|
||||
Logger::error(std::string("Failed to set initial VSync: ") + SDL_GetError());
|
||||
}
|
||||
|
||||
// Inicializar jail_audio
|
||||
JA_Init(48000, SDL_AUDIO_S16LE, 2);
|
||||
|
||||
// Obtener directorio de recursos
|
||||
std::string resources_dir = getResourcesDirectory();
|
||||
|
||||
// Inicializar generador de números aleatorios
|
||||
srand(static_cast<unsigned int>(time(nullptr)));
|
||||
|
||||
// Escanear directorio de música
|
||||
std::filesystem::path music_directory = std::filesystem::path(resources_dir) / "data" / "music";
|
||||
music_list_ = scanMusicDirectory(music_directory);
|
||||
|
||||
// Reproducir primera canción aleatoria
|
||||
if (!music_list_.empty()) {
|
||||
playRandomMusic();
|
||||
} else {
|
||||
Logger::info("No music files found in " + music_directory.string());
|
||||
}
|
||||
|
||||
// Determinar carpeta de shaders
|
||||
std::filesystem::path shaderFile(shaderPath);
|
||||
if (shaderFile.has_parent_path()) {
|
||||
@@ -391,6 +886,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
shader_start_ticks_ = SDL_GetTicks();
|
||||
fps_last_update_ticks_ = SDL_GetTicks();
|
||||
updateWindowTitle();
|
||||
|
||||
// Quad setup
|
||||
@@ -415,6 +911,27 @@ int main(int argc, char** argv) {
|
||||
bool running = true;
|
||||
|
||||
while (running) {
|
||||
// Update FPS counter
|
||||
fps_frame_count_++;
|
||||
Uint32 current_ticks = SDL_GetTicks();
|
||||
|
||||
// Update FPS display every 500ms
|
||||
if (current_ticks - fps_last_update_ticks_ >= 500) {
|
||||
float elapsed_seconds = (current_ticks - fps_last_update_ticks_) / 1000.0f;
|
||||
current_fps_ = fps_frame_count_ / elapsed_seconds;
|
||||
fps_frame_count_ = 0;
|
||||
fps_last_update_ticks_ = current_ticks;
|
||||
updateWindowTitle();
|
||||
}
|
||||
|
||||
// Actualizar audio (necesario para streaming y loops)
|
||||
JA_Update();
|
||||
|
||||
// Verificar si la música actual terminó y reproducir siguiente aleatoria
|
||||
if (!music_list_.empty() && JA_GetMusicState() == JA_MUSIC_STOPPED) {
|
||||
playRandomMusic();
|
||||
}
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_EVENT_QUIT) running = false;
|
||||
@@ -431,28 +948,81 @@ int main(int argc, char** argv) {
|
||||
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window_, &w, &h);
|
||||
|
||||
// Create/resize feedback FBO if needed
|
||||
if (current_shader_uses_feedback_) {
|
||||
if (feedback_fbo_ == 0 || current_window_width_ != w || current_window_height_ != h) {
|
||||
createFeedbackFBO(w, h);
|
||||
current_window_width_ = w;
|
||||
current_window_height_ = h;
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(current_program_);
|
||||
|
||||
// Obtener uniform locations
|
||||
GLint locRes = glGetUniformLocation(current_program_, "iResolution");
|
||||
GLint locTime = glGetUniformLocation(current_program_, "iTime");
|
||||
|
||||
float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f;
|
||||
|
||||
// === FEEDBACK RENDERING ===
|
||||
if (current_shader_uses_feedback_) {
|
||||
// Step 1: Bind feedback texture to iChannel
|
||||
std::string channelName = "iChannel" + std::to_string(feedback_channel_);
|
||||
GLint locChannel = glGetUniformLocation(current_program_, channelName.c_str());
|
||||
|
||||
if (locChannel >= 0) {
|
||||
glActiveTexture(GL_TEXTURE0 + feedback_channel_);
|
||||
glBindTexture(GL_TEXTURE_2D, feedback_texture_);
|
||||
glUniform1i(locChannel, feedback_channel_);
|
||||
}
|
||||
|
||||
// Step 2: Render to feedback FBO
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, feedback_fbo_);
|
||||
glViewport(0, 0, w, h);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUseProgram(current_program_);
|
||||
|
||||
// Obtener uniform locations (se recalculan porque el shader puede cambiar)
|
||||
GLint locRes = glGetUniformLocation(current_program_, "iResolution");
|
||||
GLint locTime = glGetUniformLocation(current_program_, "iTime");
|
||||
|
||||
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||
if (locTime >= 0) {
|
||||
float t = (SDL_GetTicks() - shader_start_ticks_) / 1000.0f;
|
||||
glUniform1f(locTime, t);
|
||||
}
|
||||
if (locTime >= 0) glUniform1f(locTime, t);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Step 3: Render to screen (using the same FBO texture)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||
if (locTime >= 0) glUniform1f(locTime, t);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Unbind texture
|
||||
glActiveTexture(GL_TEXTURE0 + feedback_channel_);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} else {
|
||||
// === NORMAL RENDERING (no feedback) ===
|
||||
glViewport(0, 0, w, h);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (locRes >= 0) glUniform2f(locRes, float(w), float(h));
|
||||
if (locTime >= 0) glUniform1f(locTime, t);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
SDL_GL_SwapWindow(window_);
|
||||
SDL_Delay(1);
|
||||
if (!Options_video.vsync) {
|
||||
SDL_Delay(1); // Prevent CPU spinning when vsync is off
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
@@ -462,6 +1032,16 @@ int main(int argc, char** argv) {
|
||||
glDeleteProgram(current_program_);
|
||||
}
|
||||
|
||||
// Cleanup feedback FBO
|
||||
destroyFeedbackFBO();
|
||||
|
||||
// Cleanup audio
|
||||
if (current_music_) {
|
||||
JA_DeleteMusic(current_music_);
|
||||
current_music_ = nullptr;
|
||||
}
|
||||
JA_Quit();
|
||||
|
||||
SDL_GL_DestroyContext(glContext);
|
||||
SDL_DestroyWindow(window_);
|
||||
SDL_Quit();
|
||||
|
||||
477
third_party/jail_audio.cpp
vendored
Normal file
477
third_party/jail_audio.cpp
vendored
Normal file
@@ -0,0 +1,477 @@
|
||||
#ifndef JA_USESDLMIXER
|
||||
#include "jail_audio.h"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_SetAudioStreamGain, SDL_PutAudioStreamData, SDL_DestroyAudioStream, SDL_GetAudioStreamAvailable, Uint8, SDL_CreateAudioStream, SDL_UnbindAudioStream, Uint32, SDL_CloseAudioDevice, SDL_GetTicks, SDL_Log, SDL_free, SDL_AudioSpec, SDL_AudioStream, SDL_IOFromMem, SDL_LoadWAV, SDL_LoadWAV_IO, SDL_OpenAudioDevice, SDL_clamp, SDL_malloc, SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, SDL_AudioDeviceID, SDL_memcpy
|
||||
#include <stdint.h> // Para uint32_t, uint8_t
|
||||
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
|
||||
#include <stdlib.h> // Para free, malloc
|
||||
#include <string.h> // Para strcpy, strlen
|
||||
|
||||
#include "stb_vorbis.h" // Para stb_vorbis_decode_memory
|
||||
|
||||
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
|
||||
#define JA_MAX_GROUPS 2
|
||||
|
||||
struct JA_Sound_t
|
||||
{
|
||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
||||
Uint32 length { 0 };
|
||||
Uint8 *buffer { NULL };
|
||||
};
|
||||
|
||||
struct JA_Channel_t
|
||||
{
|
||||
JA_Sound_t *sound { nullptr };
|
||||
int pos { 0 };
|
||||
int times { 0 };
|
||||
int group { 0 };
|
||||
SDL_AudioStream *stream { nullptr };
|
||||
JA_Channel_state state { JA_CHANNEL_FREE };
|
||||
};
|
||||
|
||||
struct JA_Music_t
|
||||
{
|
||||
SDL_AudioSpec spec { SDL_AUDIO_S16, 2, 48000 };
|
||||
Uint32 length { 0 };
|
||||
Uint8 *buffer { nullptr };
|
||||
char *filename { nullptr };
|
||||
|
||||
int pos { 0 };
|
||||
int times { 0 };
|
||||
SDL_AudioStream *stream { nullptr };
|
||||
JA_Music_state state { JA_MUSIC_INVALID };
|
||||
};
|
||||
|
||||
JA_Music_t *current_music { nullptr };
|
||||
JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
|
||||
|
||||
SDL_AudioSpec JA_audioSpec { SDL_AUDIO_S16, 2, 48000 };
|
||||
float JA_musicVolume { 1.0f };
|
||||
float JA_soundVolume[JA_MAX_GROUPS];
|
||||
bool JA_musicEnabled { true };
|
||||
bool JA_soundEnabled { true };
|
||||
SDL_AudioDeviceID sdlAudioDevice { 0 };
|
||||
//SDL_TimerID JA_timerID { 0 };
|
||||
|
||||
bool fading = false;
|
||||
int fade_start_time;
|
||||
int fade_duration;
|
||||
int fade_initial_volume;
|
||||
|
||||
|
||||
void JA_Update()
|
||||
{
|
||||
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING)
|
||||
{
|
||||
if (fading) {
|
||||
int time = SDL_GetTicks();
|
||||
if (time > (fade_start_time+fade_duration)) {
|
||||
fading = false;
|
||||
JA_StopMusic();
|
||||
return;
|
||||
} else {
|
||||
const int time_passed = time - fade_start_time;
|
||||
const float percent = (float)time_passed / (float)fade_duration;
|
||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume*(1.0 - percent));
|
||||
}
|
||||
}
|
||||
|
||||
if (current_music->times != 0)
|
||||
{
|
||||
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length/2)) {
|
||||
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
|
||||
}
|
||||
if (current_music->times>0) current_music->times--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) JA_StopMusic();
|
||||
}
|
||||
}
|
||||
|
||||
if (JA_soundEnabled)
|
||||
{
|
||||
for (int i=0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
if (channels[i].times != 0)
|
||||
{
|
||||
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length/2)) {
|
||||
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
|
||||
if (channels[i].times>0) channels[i].times--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
JA_audioSpec = {format, num_channels, freq };
|
||||
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
|
||||
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
|
||||
if (sdlAudioDevice==0) SDL_Log("Failed to initialize SDL audio!");
|
||||
for (int i=0; i<JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
|
||||
for (int i=0; i<JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
|
||||
}
|
||||
|
||||
void JA_Quit()
|
||||
{
|
||||
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
|
||||
sdlAudioDevice = 0;
|
||||
}
|
||||
|
||||
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length)
|
||||
{
|
||||
JA_Music_t *music = new JA_Music_t();
|
||||
|
||||
int chan, samplerate;
|
||||
short *output;
|
||||
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
|
||||
|
||||
music->spec.channels = chan;
|
||||
music->spec.freq = samplerate;
|
||||
music->spec.format = SDL_AUDIO_S16;
|
||||
music->buffer = (Uint8*)SDL_malloc(music->length);
|
||||
SDL_memcpy(music->buffer, output, music->length);
|
||||
free(output);
|
||||
music->pos = 0;
|
||||
music->state = JA_MUSIC_STOPPED;
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
JA_Music_t *JA_LoadMusic(const char* filename)
|
||||
{
|
||||
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
|
||||
FILE *f = fopen(filename, "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
Uint8 *buffer = (Uint8*)malloc(fsize + 1);
|
||||
if (fread(buffer, fsize, 1, f)!=1) return NULL;
|
||||
fclose(f);
|
||||
|
||||
JA_Music_t *music = JA_LoadMusic(buffer, fsize);
|
||||
music->filename = (char*)malloc(strlen(filename)+1);
|
||||
strcpy(music->filename, filename);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
void JA_PlayMusic(JA_Music_t *music, const int loop)
|
||||
{
|
||||
if (!JA_musicEnabled) return;
|
||||
|
||||
JA_StopMusic();
|
||||
|
||||
current_music = music;
|
||||
current_music->pos = 0;
|
||||
current_music->state = JA_MUSIC_PLAYING;
|
||||
current_music->times = loop;
|
||||
|
||||
current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec);
|
||||
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length)) printf("[ERROR] SDL_PutAudioStreamData failed!\n");
|
||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n");
|
||||
//SDL_ResumeAudioStreamDevice(current_music->stream);
|
||||
}
|
||||
|
||||
char *JA_GetMusicFilename(JA_Music_t *music)
|
||||
{
|
||||
if (!music) music = current_music;
|
||||
return music->filename;
|
||||
}
|
||||
|
||||
void JA_PauseMusic()
|
||||
{
|
||||
if (!JA_musicEnabled) return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
||||
|
||||
current_music->state = JA_MUSIC_PAUSED;
|
||||
//SDL_PauseAudioStreamDevice(current_music->stream);
|
||||
SDL_UnbindAudioStream(current_music->stream);
|
||||
}
|
||||
|
||||
void JA_ResumeMusic()
|
||||
{
|
||||
if (!JA_musicEnabled) return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
||||
|
||||
current_music->state = JA_MUSIC_PLAYING;
|
||||
//SDL_ResumeAudioStreamDevice(current_music->stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
|
||||
}
|
||||
|
||||
void JA_StopMusic()
|
||||
{
|
||||
if (!JA_musicEnabled) return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID) return;
|
||||
|
||||
current_music->pos = 0;
|
||||
current_music->state = JA_MUSIC_STOPPED;
|
||||
//SDL_PauseAudioStreamDevice(current_music->stream);
|
||||
SDL_DestroyAudioStream(current_music->stream);
|
||||
current_music->stream = nullptr;
|
||||
free(current_music->filename);
|
||||
current_music->filename = nullptr;
|
||||
}
|
||||
|
||||
void JA_FadeOutMusic(const int milliseconds)
|
||||
{
|
||||
if (!JA_musicEnabled) return;
|
||||
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return;
|
||||
|
||||
fading = true;
|
||||
fade_start_time = SDL_GetTicks();
|
||||
fade_duration = milliseconds;
|
||||
fade_initial_volume = JA_musicVolume;
|
||||
}
|
||||
|
||||
JA_Music_state JA_GetMusicState()
|
||||
{
|
||||
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
|
||||
if (!current_music) return JA_MUSIC_INVALID;
|
||||
|
||||
return current_music->state;
|
||||
}
|
||||
|
||||
void JA_DeleteMusic(JA_Music_t *music)
|
||||
{
|
||||
if (current_music == music) current_music = nullptr;
|
||||
SDL_free(music->buffer);
|
||||
if (music->stream) SDL_DestroyAudioStream(music->stream);
|
||||
delete music;
|
||||
}
|
||||
|
||||
float JA_SetMusicVolume(float volume)
|
||||
{
|
||||
JA_musicVolume = SDL_clamp( volume, 0.0f, 1.0f );
|
||||
if (current_music) SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||
return JA_musicVolume;
|
||||
}
|
||||
|
||||
void JA_SetMusicPosition(float value)
|
||||
{
|
||||
if (!current_music) return;
|
||||
current_music->pos = value * current_music->spec.freq;
|
||||
}
|
||||
|
||||
float JA_GetMusicPosition()
|
||||
{
|
||||
if (!current_music) return 0;
|
||||
return float(current_music->pos)/float(current_music->spec.freq);
|
||||
}
|
||||
|
||||
void JA_EnableMusic(const bool value)
|
||||
{
|
||||
if ( !value && current_music && (current_music->state==JA_MUSIC_PLAYING) ) JA_StopMusic();
|
||||
|
||||
JA_musicEnabled = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
sound->buffer = buffer;
|
||||
sound->length = length;
|
||||
return sound;
|
||||
}
|
||||
|
||||
JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size),1, &sound->spec, &sound->buffer, &sound->length);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
JA_Sound_t *JA_LoadSound(const char* filename)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group)
|
||||
{
|
||||
if (!JA_soundEnabled) return -1;
|
||||
|
||||
int channel = 0;
|
||||
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
|
||||
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) channel = 0;
|
||||
JA_StopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group)
|
||||
{
|
||||
if (!JA_soundEnabled) return -1;
|
||||
|
||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
|
||||
JA_StopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void JA_DeleteSound(JA_Sound_t *sound)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||
if (channels[i].sound == sound) JA_StopChannel(i);
|
||||
}
|
||||
SDL_free(sound->buffer);
|
||||
delete sound;
|
||||
}
|
||||
|
||||
void JA_PauseChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
channels[i].state = JA_CHANNEL_PAUSED;
|
||||
//SDL_PauseAudioStreamDevice(channels[i].stream);
|
||||
SDL_UnbindAudioStream(channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
channels[channel].state = JA_CHANNEL_PAUSED;
|
||||
//SDL_PauseAudioStreamDevice(channels[channel].stream);
|
||||
SDL_UnbindAudioStream(channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JA_ResumeChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if (channels[i].state == JA_CHANNEL_PAUSED)
|
||||
{
|
||||
channels[i].state = JA_CHANNEL_PLAYING;
|
||||
//SDL_ResumeAudioStreamDevice(channels[i].stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == JA_CHANNEL_PAUSED)
|
||||
{
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
//SDL_ResumeAudioStreamDevice(channels[channel].stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JA_StopChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled) return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||
if (channels[i].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[i].stream);
|
||||
channels[i].stream = nullptr;
|
||||
channels[i].state = JA_CHANNEL_FREE;
|
||||
channels[i].pos = 0;
|
||||
channels[i].sound = NULL;
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[channel].stream);
|
||||
channels[channel].stream = nullptr;
|
||||
channels[channel].state = JA_CHANNEL_FREE;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].sound = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JA_Channel_state JA_GetChannelState(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled) return JA_SOUND_DISABLED;
|
||||
|
||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return JA_CHANNEL_INVALID;
|
||||
|
||||
return channels[channel].state;
|
||||
}
|
||||
|
||||
float JA_SetSoundVolume(float volume, const int group)
|
||||
{
|
||||
const float v = SDL_clamp( volume, 0.0f, 1.0f );
|
||||
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
|
||||
if (group==-1 || group==i) JA_soundVolume[i]=v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if ( ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) &&
|
||||
((group==-1) || (channels[i].group==group)) )
|
||||
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[i]);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void JA_EnableSound(const bool value)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
{
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i);
|
||||
}
|
||||
JA_soundEnabled = value;
|
||||
}
|
||||
|
||||
float JA_SetVolume(float volume)
|
||||
{
|
||||
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
|
||||
|
||||
return JA_musicVolume;
|
||||
}
|
||||
|
||||
#endif
|
||||
43
third_party/jail_audio.h
vendored
Normal file
43
third_party/jail_audio.h
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED };
|
||||
enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED, JA_MUSIC_DISABLED };
|
||||
|
||||
struct JA_Sound_t;
|
||||
struct JA_Music_t;
|
||||
|
||||
void JA_Update();
|
||||
|
||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels);
|
||||
void JA_Quit();
|
||||
|
||||
JA_Music_t *JA_LoadMusic(const char* filename);
|
||||
JA_Music_t *JA_LoadMusic(Uint8* buffer, Uint32 length);
|
||||
void JA_PlayMusic(JA_Music_t *music, const int loop = -1);
|
||||
char *JA_GetMusicFilename(JA_Music_t *music = nullptr);
|
||||
void JA_PauseMusic();
|
||||
void JA_ResumeMusic();
|
||||
void JA_StopMusic();
|
||||
void JA_FadeOutMusic(const int milliseconds);
|
||||
JA_Music_state JA_GetMusicState();
|
||||
void JA_DeleteMusic(JA_Music_t *music);
|
||||
float JA_SetMusicVolume(float volume);
|
||||
void JA_SetMusicPosition(float value);
|
||||
float JA_GetMusicPosition();
|
||||
void JA_EnableMusic(const bool value);
|
||||
|
||||
JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length);
|
||||
JA_Sound_t *JA_LoadSound(Uint8* buffer, Uint32 length);
|
||||
JA_Sound_t *JA_LoadSound(const char* filename);
|
||||
int JA_PlaySound(JA_Sound_t *sound, const int loop = 0, const int group=0);
|
||||
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0, const int group=0);
|
||||
void JA_PauseChannel(const int channel);
|
||||
void JA_ResumeChannel(const int channel);
|
||||
void JA_StopChannel(const int channel);
|
||||
JA_Channel_state JA_GetChannelState(const int channel);
|
||||
void JA_DeleteSound(JA_Sound_t *sound);
|
||||
float JA_SetSoundVolume(float volume, const int group=0);
|
||||
void JA_EnableSound(const bool value);
|
||||
|
||||
float JA_SetVolume(float volume);
|
||||
5631
third_party/stb_vorbis.h
vendored
Normal file
5631
third_party/stb_vorbis.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user